OSCR

Evolutionary stasis in synapsid encephalization during the end-permian mass extinction.

Code ↔ Paper

6 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 6 matches
  1. [1] § Phylogenetic comparative analyses ↔ Tree_calibration_Benoitetal_2026.Rmd, lines 76–120 · score 0.84 · consensus.edges, multi2di, extinction rates, calibrated trees, durationfreqcont, cal3
  2. [2] § Phylogenetic comparative analyses ↔ Script_Benoitetal_2026.Rmd, lines 1002–1041 · score 0.75 · RRphylo, terminal taxa, Biarmosuchia, Dinocephalia, Gorgonopsia, Therocephalia
  3. [3] § Phylogenetic comparative analyses ↔ Script_Benoitetal_2026.Rmd, lines 213–260 · score 0.67 · Akaike Information Criterion, finite sample, evolutionary models, Pagel, body mass, Brownian
  4. [4] § Phylogenetic comparative analyses ↔ Script_Benoitetal_2026.Rmd, lines 85–108 · score 0.63 · lambda model, best fit, Phylopars, imputation, imputed, BIC
  5. [5] § Phylogenetic comparative analyses ↔ Script_Benoitetal_2026.Rmd, lines 262–307 · score 0.62 · extremely low, Phylogenetic signal, weight, body mass, coefficient, AICc
  6. [6] § Phylogenetic comparative analyses ↔ Script_Benoitetal_2026.Rmd, lines 213–260 · score 0.62 · variance covariance, evolutionary models, PGLS, body mass, coefficient, AICc

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

R Markdown · 1,096 lines · 50 KB · no license · 5 matches

  1. ---
  2. title: "Script Benoit et al. 2026"
  3. author: "Lucas Legendre"
  4. date: "`r Sys.Date()`"
  5. output:
  6. html_document:
  7. df_print: paged
  8. toc_float:
  9. collapsed: false
  10. smooth_scroll: false
  11. toc: true
  12. ---
  13. ```{r setup, include=FALSE}
  14. knitr::opts_chunk$set(echo = TRUE)
  15. ```
  16. Compiled under R version 4.5.3 (2026-03-11)
  17. <b>WARNING</b>: edit the working directory to your preferred folder.
  18. This document details all analyses performed in R for the study:
  19. Benoit J., Legendre L.J., Midzuk A., Fernandez V., Araújo R., Browning C. Abdala F., Botha J., Angielczyk K. 2026. Evolutionary stasis in synapsid encephalization during the End-Permian mass extinction. *Scientific Reports*.
  20. For more information regarding the study, datasets, and analyses, please refer to the Main Text and Supplementary Information of this paper. If you have any additional questions, feel free to email me at [lucasjlegendre\@gmail.com](mailto:[email hidden]){.email}.
  21. ### Loading packages
  22. ```{r, message = FALSE}
  23. library(ape)
  24. library(nlme)
  25. library(phytools)
  26. library(phylolm)
  27. library(geiger)
  28. library(windex)
  29. library(AICcmodavg)
  30. library(evobiR)
  31. library(tidyverse)
  32. library(RRphylo)
  33. library(Rphylopars)
  34. library(hrbrthemes)
  35. library(RColorBrewer)
  36. library(strap)
  37. library(slouch)
  38. library(scales)
  39. ```
  40. ## Seed and cores
  41. ```{r}
  42. set.seed(101)
  43. cc <- max(1, floor(0.5 * parallel::detectCores()))
  44. ```
  45. ## Data and tree
  46. - Data
  47. ```{r}
  48. maindata<-read.csv("maindataNew.csv", header=TRUE, sep=";")
  49. ```
  50. - Tree
  51. ```{r}
  52. # Calibrated tree (see separate tree calibration script)
  53. ttree<-read.nexus("CalibratedTreeNew.nex")
  54. plotTree(ttree, fsize=0.5, lwd=1, ftype="i")
  55. ttree$root.time<-263
  56. setdiff(ttree$tip.label, maindata$Taxon)
  57. setdiff(maindata$Taxon, ttree$tip.label)
  58. # Perfect match between tip labels and taxa in the dataset
  59. # Reorder data
  60. maindata2<-ReorderData(ttree, maindata, taxa.names=1)
  61. rownames(maindata2)<-maindata2$Taxon
  62. ```
  63. ## Preliminary analyses – evolutionary models, phenograms, ancestral reconstructions
  64. - Log-conversion
  65. ```{r}
  66. logmain<-maindata2[,c("Taxon", "Order", "Body_mass", "EQ_noOB", "EQ_OB")]
  67. logmain[,3:5]<-log1p(logmain[,3:5]) # log1p to get only positive values
  68. ```
  69. - Predict missing data in the dataset, using `Rphylopars` (Goolsby et al., 2017)
  70. ```{r}
  71. # Data
  72. parsdata<-logmain[,c(1,4,5)]
  73. colnames(parsdata)[1]<-"species"
  74. # Imputation
  75. modelpars<-c("BM", "lambda", "EB", "star") # Evolutionary models
  76. PPE<-list(); BICdat<-data.frame()
  77. for (i in 1:length(modelpars)) {
  78. PPE[[i]]<-phylopars(trait_data = parsdata, tree = ttree, model=modelpars[i])
  79. BICdat[i,1]<-BIC(PPE[[i]]) # BIC to compare models
  80. }
  81. rownames(BICdat)<-modelpars; colnames(BICdat)<-"BIC"
  82. BICdat # The lambda model is the best fit
  83. # Extract imputed values and add them to the main dataset
  84. imp <- PPE[[which.min(BICdat$BIC)]]$anc_recon[1:nrow(logmain),
  85. c("EQ_noOB","EQ_OB")]
  86. for(tr in c("EQ_noOB","EQ_OB")) {
  87. idx_na <- is.na(logmain[[tr]])
  88. logmain[[tr]][idx_na] <- imp[rownames(logmain)[idx_na], tr]
  89. }
  90. ```
  91. - Phylogenetic signal (Pagel's lambda) in `phytools` (Revell, 2024)
  92. ```{r}
  93. # Phylogenetic signal (Pagel's lambda)
  94. var=list(); phy=list(); phydat=data.frame()
  95. for (i in 3:5) {
  96. var<-logmain[,i]; names(var)<-rownames(logmain)
  97. var2<-var[!is.na(var)]
  98. treevar<-drop.tip(ttree,setdiff(ttree$tip.label,names(var2)))
  99. phy[[i]]<-phylosig(treevar, var2, method="lambda", test=TRUE)
  100. phydat[i,1]<-colnames(logmain)[i]; phydat[i,2]<-phy[[i]]$lambda; phydat[i,3]<-phy[[i]]$P
  101. }
  102. colnames(phydat)<-c("Variable", "Lambda", "P")
  103. phydat[c(3:5),]
  104. ```
  105. Signal is very high for body mass (λ > 0.99), high for EQ with and without olfactory bulbs (λ = 0.73 and λ = 0.70, respectively), and highly significant (p < 0.001) for all three traits.
  106. - Test for best evolutionary model, using `geiger` (Pennell et al., 2014)
  107. ```{r, warning = FALSE}
  108. models=c("BM", "OU", "EB", "rate_trend","lambda", "white")
  109. # (For more info, check '?fitContinuous')
  110. var=list(); fit=list(); mod=list()
  111. for (i in 3:5) {
  112. var<-logmain[,i]; names(var)<-rownames(logmain)
  113. var2<-var[!is.na(var)]
  114. treevar<-multi2di(drop.tip(ttree,setdiff(ttree$tip.label,names(var2))))
  115. for (m in 1:length(models)) {
  116. fit[[m]]=fitContinuous(treevar, var2, model=models[m], ncores=2)
  117. }
  118. mod[[i]]<-modSel.geiger(fit[[1]],fit[[2]],fit[[3]],fit[[4]],fit[[5]],fit[[6]])
  119. }
  120. mod
  121. ```
  122. Best model for body mass is an Early Burst model (EB). A Lambda model fits both types of EQ best – both traits follow a Brownian motion model; we can reconstruct ancestral states using such a model and map them on the tree.
  123. - Phenogram for each trait
  124. ```{r}
  125. for (i in 3:5) {
  126. trait<-logmain[,i]; names(trait)<-rownames(logmain)
  127. trait<-na.omit(trait)
  128. treena<-drop.tip(ttree, setdiff(ttree$tip.label, names(trait)))
  129. phenogram(treena, trait, ,ftype="i", spread.labels=TRUE,
  130. spread.cost=c(1,0),fsize=0.7,color=palette()[4],
  131. xlab="Time (Ma)",ylab=paste("log",colnames(logmain)[i]),cex.axis=0.8,
  132. axes=FALSE,las=1, lwd=1)
  133. axis(1,at=round(seq(0,max(nodeHeights(treena)),length.out=6),1),
  134. label=round(seq(max(nodeHeights(treena)),0,length.out=6),1),
  135. cex.axis=0.8)
  136. axis(2,las=1,cex.axis=0.8)
  137. grid()
  138. title(paste(colnames(logmain[i])))
  139. }
  140. ```
  141. Some clear shifts in all traits, but they seem to occur in Mammaliaformes and less inclusive clades.
  142. - Ancestral state reconstructions
  143. ```{r}
  144. # Custom color palette
  145. pal<-c("yellow1","orange1","orangered3","purple4")
  146. # Compile the best tree with corresponding value of lambda for both types of EQ
  147. lambdaval1<-phydat[4,2]; lambdaval2<-phydat[5,2]
  148. treeEQnoOB<-ttree; treeEQOB<-ttree
  149. treeEQnoOB<-rescale(treeEQnoOB, "lambda", lambdaval1)
  150. treeEQOB<-rescale(treeEQOB, "lambda", lambdaval2)
  151. treelist<-c(ttree, treeEQnoOB, treeEQOB)
  152. dataplot=list(); fit=list(); obj=list()
  153. for (i in c(3:5)) {
  154. dataplot[[i]]<-logmain[,i]; names(dataplot[[i]])<-rownames(logmain)
  155. dataplot[[i]]<-na.omit(dataplot[[i]])
  156. treeplot<-treelist[[i-2]]
  157. fit[[i]]<-fastAnc(treeplot, dataplot[[i]], vars=TRUE, CI=TRUE)
  158. obj[[i]]<-setMap(contMap(treeplot, dataplot[[i]], plot=FALSE),
  159. colors=pal)
  160. plot(obj[[i]], fsize=0.5, ftype="i")
  161. title(paste('Ancestral state reconstruction for', colnames(logmain)[i]))
  162. }
  163. ```
  164. As expected from phenograms, we do not see clear shifts in any major therapsid clade outside Mammaliaformes. The pattern in EQ seems to be the exact reverse of that of body mass – as lineages get smaller, their EQ increases. We can test that further for specific lineages and see whether there are any specific shifts in the tree.
  165. ### How well correlated to body mass is the EQ?
  166. We can test it using Phylogenetic Generalized Least Squares (PGLS) regressions.
  167. - Import function `fitEvolPar` to estimate the best fit of the main parameter (alpha, lambda, or g) in the corresponding evolutionary model (OU, lambda, or EB, respectively)
  168. (To download it from GitHub: <https://github.com/LucasLegendre/fitEvolPar>)
  169. ```{r}
  170. sys.source("fitEvolPar.R", envir = knitr::knit_global())
  171. ```
  172. ```{r}
  173. # Compile the VCV matrix (necessary for a non-ultrametric tree, which is always going to be the case with fossils in the sample)
  174. Wt<-diag(vcv.phylo(ttree))
  175. # Data frame for 'fitEvolPar'
  176. EQnoOBd<-logmain[,3:4]
  177. ```
  178. #### Compile individual regressions
  179. We also test for best evolutionary model in the variance-covariance (VCV) matrix in the error term, using the Akaike Information Criterion corrected for finite sample sizes (AICc).
  180. - For EQ with no OB
  181. ```{r, warning=FALSE}
  182. # PGLS models
  183. BM<-gls(EQ_noOB~Body_mass, data=logmain,
  184. correlation=corBrownian(phy=ttree, form=~1),
  185. weights=varFixed(~Wt), method="ML")
  186. OU<-gls(EQ_noOB~Body_mass, data=logmain,
  187. correlation=corMartins(0.1, phy=ttree, form=~1),
  188. weights=varFixed(~Wt), method="ML")
  189. Lambda<-gls(EQ_noOB~Body_mass, data=logmain,
  190. correlation=corPagel(1, phy=ttree, form=~1),
  191. weights=varFixed(~Wt), method="ML")
  192. EB<-gls(EQ_noOB~Body_mass, data=logmain,
  193. correlation=corBlomberg(fitEvolPar(EQnoOBd, ttree,"EB"),
  194. phy=ttree, fixed = TRUE, form=~1),
  195. weights=varFixed(~Wt), method="ML")
  196. OLS<-gls(EQ_noOB~Body_mass, data=logmain, method="ML")
  197. # AICc
  198. Cand.models = list()
  199. Cand.models[[1]] = BM
  200. Cand.models[[2]] = OU
  201. Cand.models[[3]] = Lambda
  202. Cand.models[[4]] = EB
  203. Cand.models[[5]] = OLS
  204. Modnames = paste(c("BM", "OU", "Lambda", "EB", "OLS"), sep = " ")
  205. aictab(cand.set = Cand.models, modnames = Modnames, sort = T)
  206. # Best model
  207. best1<-lm(EQ_noOB~Body_mass, data=logmain)
  208. summary(best1)
  209. # Plot
  210. ggplot(logmain, aes(Body_mass, EQ_noOB, color=Order)) +
  211. geom_point(size=5) +
  212. # geom_text(aes(label=Taxon), hjust=-0.1, vjust=0.4) +
  213. xlab("ln body mass (g)") +
  214. ylab("ln EQ (no OB)") +
  215. geom_abline(intercept=best1$coefficients[1], slope=best1$coefficients[2],
  216. colour="royalblue", linewidth=1.3) +
  217. scale_color_brewer(palette="Dark2") +
  218. theme_ipsum(axis_title_size=15, base_size = 20, axis_text_size = 12)
  219. ```
  220. Best model is a white model (i.e. no phylogenetic signal). The model is significant (p < 0.01), but the pseudo R-squared is extremely low (R^2 = 0.05). Looking at the plot, there does indeed not seem to be any clear effect of body mass on the EQ.
  221. - For EQ with OB
  222. ```{r, warning=FALSE}
  223. # PGLS models
  224. BM<-gls(EQ_OB~Body_mass, data=logmain,
  225. correlation=corBrownian(phy=ttree, form=~1),
  226. weights=varFixed(~Wt), method="ML")
  227. OU<-gls(EQ_OB~Body_mass, data=logmain,
  228. correlation=corMartins(0.1, phy=ttree, form=~1),
  229. weights=varFixed(~Wt), method="ML")
  230. Lambda<-gls(EQ_OB~Body_mass, data=logmain,
  231. correlation=corPagel(1, phy=ttree, form=~1),
  232. weights=varFixed(~Wt), method="ML")
  233. EB<-gls(EQ_OB~Body_mass, data=logmain,
  234. correlation=corBlomberg(fitEvolPar(EQnoOBd, ttree,"EB"),
  235. phy=ttree, fixed = TRUE, form=~1),
  236. weights=varFixed(~Wt), method="ML")
  237. OLS<-gls(EQ_OB~Body_mass, data=logmain, method="ML")
  238. # AICc
  239. Cand.models = list()
  240. Cand.models[[1]] = BM
  241. Cand.models[[2]] = OU
  242. Cand.models[[3]] = Lambda
  243. Cand.models[[4]] = EB
  244. Cand.models[[5]] = OLS
  245. Modnames = paste(c("BM", "OU", "Lambda", "EB", "OLS"), sep = " ")
  246. aictab(cand.set = Cand.models, modnames = Modnames, sort = T)
  247. # Best model
  248. best2<-lm(EQ_OB~Body_mass, data=logmain)
  249. summary(best2)
  250. # Plot
  251. ggplot(logmain, aes(Body_mass, EQ_OB, color=Order)) +
  252. geom_point(size=5) +
  253. # geom_text(aes(label=Taxon), hjust=-0.1, vjust=0.4) +
  254. xlab("ln body mass (g)") +
  255. ylab("ln EQ (OB)") +
  256. geom_abline(intercept=best2$coefficients[1], slope=best2$coefficients[2],
  257. colour="royalblue", linewidth=1.3) +
  258. scale_color_brewer(palette="Dark2") +
  259. theme_ipsum(axis_title_size=15, base_size = 20, axis_text_size = 12)
  260. ```
  261. Similar result as without OB: the regression is significant (p < 0.01), but R-squared is very low (R^2 = 0.07), and there is clearly no signal in the plot.
  262. ## Evolutionary rates and testing for shifts, using `RRphylo`
  263. More info on the method (phylogenetic ridge regressions) and how to use it in the very detailed help section and tutorials of this package; see also Kratsch & McHardy (2014) and Castiglione et al. (2018, 2019).
  264. Since we did not recover body mass as a good predictor of EQ, we will not use it here as a covariate to look at the evolution of EQ and test for shifts in values and/or evolutionary rates.
  265. ### For EQ with no OB
  266. - Prepare the data and tree
  267. ```{r}
  268. # Dataset
  269. EQ1<-logmain[,4]; names(EQ1)<-rownames(logmain)
  270. # Proportion of clusters
  271. cc <- 2/parallel::detectCores()
  272. # Compile RRphylo
  273. RREQ1<-RRphylo(tree=ttree, y=EQ1, clus=cc)
  274. # To visualize node numbers in the tree
  275. nodetree<-ttree; nodetree$edge.length<-rep(1, 250) # Version of the tree where all branches have a length of 1 (easier to visualize)
  276. plotTree(nodetree, fsize=0.5, lwd=1, ftype="i")
  277. labelnodes(text=1:ttree$Nnode,node=1:ttree$Nnode+Ntip(ttree),
  278. interactive=FALSE,circle.exp=0.4,cex=0.5)
  279. ```
  280. #### Let's test for significant shifts in evolutionary rates in the tree, using `search.shift`
  281. - In the whole tree (as a first step, to see if any main trends can be identified)
  282. ```{r}
  283. shiftsWholeR<-search.shift(RREQ1, status.type= "clade")
  284. shiftsWholeR$all.clades
  285. as.numeric(rownames(shiftsWholeR$all.clades))-length(ttree$tip.label) # actual node numbers as visualized in plotTree
  286. # Significant shifts
  287. posshift<-as.numeric(names(which(shiftsWholeR$all.clades[,2]>0.975)))
  288. posshift # Significant positive shifts
  289. negshift<-as.numeric(names(which(shiftsWholeR$all.clades[,2]<0.025)))
  290. negshift # Significant negative shifts
  291. # Plot them on the tree
  292. plotTree(nodetree, fsize=0.5, lwd=1, ftype="i")
  293. labelnodes(text=1:ttree$Nnode,node=1:ttree$Nnode+Ntip(ttree),
  294. interactive=FALSE,circle.exp=0.4,cex=0.5)
  295. nodelabels(text="+", node=posshift, adj=c(1.5, 1), frame="none", col="red2")
  296. nodelabels(text="-", node=negshift, adj=c(1.5, 1), frame="none", col="steelblue2")
  297. ```
  298. **Two major series of shifts, both in Eucynodontia**: higher rates in Cynognathia (suggesting a selective pressure on EQ in that clade), and lower rates in Probainognathia (suggesting relaxed constraints, potentially associated with ancestral stabilizing selection). Eucynodontia and the immediately more inclusive clade (with *Platycraniellus*) also show a shift towards a lower rate, suggesting **the trend in Probainognathia is ancestral to eucynodonts**, whereas **that in Cynognathia is a later, distinct shift**.
  299. - Let's make a phylomorphospace for EQ with no OB and body mass, with colors for these two clades:
  300. ```{r}
  301. # Data
  302. EQnoOBdat<-logmain[,3:4]
  303. # Basic phylomorphospace, if you need it – remove the 'label="off"' part if you want taxa names, but it gets very crowded!
  304. par(mar = c(5.1, 4.1, 4.1, 2.1))
  305. phylomorphospace(ttree,EQnoOBdat,bty="l",label="off",xlab="log (Body mass) (g)",
  306. ylab="log (EQ) (no OB)",node.size=c(0,1))
  307. title(main="Phylomorphospace plot",font.main=3)
  308. # Basic tree with labelled clades
  309. plotTree(ttree,fsize=0.5,ftype="i")
  310. nodelabels(frame="circ",bg="white",cex=0.3)
  311. cladelabels(ttree,c("Probainognathia","Cynognathia"),c(144,179))
  312. # Define colors for the tree
  313. painted<-paintSubTree(ttree, 144, "Probainognathia")
  314. painted<-paintSubTree(painted, 179, "Cynognathia")
  315. # Plot it on the tree
  316. plot(painted, fsize=0.5, ftype="i")
  317. cladelabels(ttree, c("Probainognathia","Cynognathia"), c(144,179), offset=0.5)
  318. # Plot it on the phylomorphospace
  319. par(mar = c(5.1, 4.1, 4.1, 2.1))
  320. phylomorphospace(painted,EQnoOBdat,bty="l",label="off",xlab="log (Body mass) (g)",
  321. ylab="log (EQ) (no OB)",node.size=c(0,1.2),node.by.map=TRUE)
  322. title(main="Phylomorphospace plot",font.main=3)
  323. legend(x="topleft",legend=c("Cynognathia","Probainognathia"),
  324. pch=21,pt.cex=1.5,pt.bg=palette()[2:4],bty="n")
  325. # Plot it on the phenogram
  326. EQnoOB<-logmain[,4]; names(EQnoOB)<-rownames(logmain)
  327. par(mar = c(5.1, 4.1, 4.1, 2.1))
  328. phenogram(painted, EQnoOB, ,ftype="off", spread.labels=TRUE,
  329. spread.cost=c(1,0),fsize=0.7,
  330. xlab="Time (Ma)",ylab="log (EQ) (no OB)",cex.axis=0.8,
  331. axes=FALSE,las=1, lwd=1)
  332. axis(1,at=round(seq(0,max(nodeHeights(painted)),length.out=6),1),
  333. label=round(seq(max(nodeHeights(painted)),0,length.out=6),1),
  334. cex.axis=0.8)
  335. axis(2,las=1,cex.axis=0.8)
  336. grid()
  337. title(paste("Phenogram for EQ (no OB)"))
  338. legend(x="bottomright",legend=c("Cynognathia","Probainognathia"),
  339. pch=21,pt.cex=1.5,pt.bg=palette()[2:4],bty="n")
  340. ```
  341. As expected, Cynognathia show very high variation in a short amount of time, while the earliest nodes within Probainognathia seem to show limited variation over an extended time frame compared to more inclusive clades in the tree.
  342. Let's go a bit further... Same test, but for specific clades in the tree!
  343. We will be testing for shifts in the following clades:
  344. Biarmosuchia, Dinocephalia, Neotherapsida, Dicynodontia, Theriodontia, Gorgonopsia, Eutheriodontia, Therocephalia, Cynodontia, Eucynodontia, Cynognathia, Probainognathia, Mammaliamorpha, Mammaliaformes, Mammalia.
  345. - Specific shifts in those clades
  346. ```{r, warning=FALSE}
  347. # Node numbers
  348. nodes<-c(124,120,3,93,4,90,5,83,6,17,53,18,26,28,32)
  349. cornodes<-nodes+length(ttree$tip.label)
  350. # Look for significant shifts
  351. shiftsR<-search.shift(RREQ1, status.type= "clade", node=cornodes)
  352. shiftsR$single.clades
  353. which(shiftsR$single.clades[,2]>0.975); which(shiftsR$single.clades[,2]<0.025)
  354. posshift<-as.numeric(names(which(shiftsR$single.clades[,2]>0.975)))
  355. posshift # Significant positive shifts
  356. negshift<-as.numeric(names(which(shiftsR$single.clades[,2]<0.025)))
  357. negshift # Significant negative shifts
  358. # Plot them on the tree
  359. plotTree(nodetree, fsize=0.5, lwd=1, ftype="i")
  360. labelnodes(text=1:ttree$Nnode,node=1:ttree$Nnode+Ntip(ttree),
  361. interactive=FALSE,circle.exp=0.4,cex=0.5)
  362. #nodelabels(text="+", node=posshift, adj=c(1.5, 1), frame="none", col="red2")
  363. nodelabels(text="-", node=negshift, adj=c(1.5, 1), frame="none", col="steelblue2")
  364. ```
  365. Only the ancestral negative shift in Cynodontia, followed by similar shifts in Probainognathia, Mammaliamorpha, Mammaliaformes, and Mammalia are recovered.
  366. - We can plot the shifts on the tree
  367. ```{r}
  368. RRplotR<-plotRR(RREQ1, y=EQnoOB, multivariate = "rates")
  369. ## Recompile individual shifts for the plot
  370. posshift<-as.numeric(names(which(shiftsWholeR$all.clades[,2]>0.975)))
  371. negshift<-as.numeric(names(which(shiftsWholeR$all.clades[,2]<0.025)))
  372. EQ1shift<-search.shift(RREQ1, status.type= "clade", node=c(posshift, negshift))
  373. EQ1shift$single.clades<-as.data.frame(EQ1shift$single.clades)
  374. which(EQ1shift$single.clades[,2]>0.975); which(EQ1shift$single.clades[,2]<0.025)
  375. ## Values of EQ
  376. RRplotR$plotRRphen(variable=1, tree.args=list(no.margin=TRUE),
  377. colorbar.args=list(x=170, y=90, title.pos="bottom"))
  378. ## Rates and rate shifts on the same plot
  379. RRplotR$plotRRrates(variable=1, tree.args=list(no.margin=TRUE),
  380. colorbar.args=list(x=170, y=90, title.pos="bottom"))
  381. addShift(EQ1shift, symbols.args=list(lwd=2,fg=c(pos="red2",neg="steelblue2"),
  382. bg=scales::alpha(c(rep("red2",length(posshift)),
  383. rep("steelblue2",length(negshift))),0.3)))
  384. ## Only rate shifts
  385. plotShiftR<-plotShift(RREQ1, EQ1shift)
  386. plotShiftR$plotClades(tree.args=list(no.margin=TRUE),
  387. symbols.args=list(lwd=2,fg=c(pos="red2",neg="steelblue2"),
  388. bg=scales::alpha(c(rep("red2",length(posshift)),
  389. rep("steelblue2",length(negshift))),0.3)))
  390. ```
  391. - Finally, let's test for the significance of those shifts with jacknife/multiple permutation tests, using `overfitRR`. We use a threshold of 0.75 (3/4 of simulated trees recovered with the same shift) to assess a shift as significant.
  392. ```{r}
  393. # Generate alternative topologies
  394. resampleEQ1<-resampleTree(RREQ1$tree, node=c(posshift, negshift), nsim=1000)
  395. # Recompile regressions for the new trees
  396. overfitEQ1<-overfitRR(RREQ1, y=EQnoOB, phylo.list=resampleEQ1, clus=cc)
  397. # Test for shift significance with new topologies
  398. overshift1<-overfitSS(RR=RREQ1, oveRR=overfitEQ1, node=148)
  399. overshift2<-overfitSS(RR=RREQ1, oveRR=overfitEQ1, node=c(186,143))
  400. overshift3<-overfitSS(RR=RREQ1, oveRR=overfitEQ1, node=c(150,144))
  401. overshift4<-overfitSS(RR=RREQ1, oveRR=overfitEQ1, node=c(151,145))
  402. overshift5<-overfitSS(RR=RREQ1, oveRR=overfitEQ1, node=c(152,142))
  403. overshift6<-overfitSS(RR=RREQ1, oveRR=overfitEQ1, node=c(153,147))
  404. overshift7<-overfitSS(RR=RREQ1, oveRR=overfitEQ1, node=c(154,185))
  405. overshift8<-overfitSS(RR=RREQ1, oveRR=overfitEQ1, node=c(155,149))
  406. overshift9<-overfitSS(RR=RREQ1, oveRR=overfitEQ1, node=c(156,146))
  407. # Display the results
  408. overshift1$shift.results$clade
  409. overshift2$shift.results$clade
  410. overshift3$shift.results$clade
  411. overshift4$shift.results$clade
  412. overshift5$shift.results$clade
  413. overshift6$shift.results$clade
  414. overshift7$shift.results$clade
  415. overshift8$shift.results$clade
  416. overshift9$shift.results$clade
  417. ```
  418. Nodes 16 through 30 (i.e. Probainognathia through Mammaliaformes) all have significant negative shifts, whereas all other nodes (i.e. clades within Cynognathia) do not.
  419. => **Only the rate shifts in Probainognathia (negative shifts, i.e. stabilizing selection of EQ) are recovered as significant.**
  420. Now that we have identified shifts in evolutionary rates, what about phenotypic trends (i.e. changes in the actual values of EQ through time)?
  421. #### Testing for significant phenotypic trends for EQ, using `search.trend`
  422. See the vignette and help section of the function, as well as Castiglione et al. (2019). We are testing for significant deviations from a Brownian Motion (BM) model in the value of our trait of interest (EQ).
  423. - For the whole tree
  424. ```{r}
  425. ST<-search.trend(RR=RREQ1, y=EQnoOB, nsim=1000)
  426. # Display the results
  427. rateres<-cbind(ST$rate.regression[1,1:4], NA); colnames(rateres)[5]<-"dev"
  428. phenres<-c(ST$phenotypic.regression[1,1:3], NA, ST$phenotypic.regression[1,4])
  429. names(phenres)[c(4,5)]<-c("spread", "dev")
  430. STres<-rbind(rateres, phenres)
  431. rownames(STres)<-c("rescaled absolute rate regression", "phenotypic regression")
  432. STres
  433. ```
  434. The absolute rate regression is not significant (p.random < 0.95), meaning the rates for the whole tree are not significantly different than expected under BM (makes sense, since we only identified localized shifts in the tree earlier). Accordingly, the 'spread' metric is very close to 1 (the expected value under BM).
  435. However, **the phenotypic regression is highly significant** (p.random = 1). The "dev" metric quantifies the deviation of the phenotypic mean of our trait from the root value, expressed in standard deviations of the distribution. Here, dev = 1.85, meaning that the value of EQ is almost twice as high as the standard deviation expected from BM. *There is a strong trend in our phenotype!* Since the slope is positive, this means we have a significant increase in EQ somewhere in the tree.
  436. - Let's test the same nodes we tested earlier for shifts
  437. ```{r}
  438. STclade1<-search.trend(RR=RREQ1, y=EQnoOB, node=c(250,246,129), nsim=1000)
  439. STclade2<-search.trend(RR=RREQ1, y=EQnoOB, node=c(219,130,216), nsim=1000)
  440. STclade3<-search.trend(RR=RREQ1, y=EQnoOB, node=c(131,209,179), nsim=1000)
  441. STclade4<-search.trend(RR=RREQ1, y=EQnoOB, node=c(143,132,144), nsim=1000)
  442. STclade5<-search.trend(RR=RREQ1, y=EQnoOB, node=c(152,154,158), nsim=1000)
  443. # Plot trends in EQ value on the tree
  444. plotTree(nodetree, fsize=0.5, lwd=1, ftype="i")
  445. labelnodes(text=1:RREQ1$tree$Nnode,node=1:RREQ1$tree$Nnode+Ntip(RREQ1$tree),
  446. interactive=FALSE,circle.exp=0.4,cex=0.5)
  447. nodelabels(text="EQ+", node=c(129, 130, 131, 132, 143, 144, 152), adj=c(1.1, 1), frame="none", col="red2")
  448. # To get the values of EQ at nodes with significant shifts
  449. values<-as.data.frame(cbind(as.numeric(rownames(RREQ1$aces)), expm1(RREQ1$aces)))
  450. Ivalues<-values %>% filter (V1 %in% c(129:132, 143, 144, 152))
  451. rownames(Ivalues)<-c("Neotherapsida", "Theriodontia", "Eutheriodontia",
  452. "Cynodontia", "Eucynodontia", "Probainognathia",
  453. "Mammaliamorpha")
  454. colnames(Ivalues)<-c("Node number", "EQ")
  455. Ivalues
  456. ```
  457. For absolute values of EQ, we have **two trends of significant increases**:
  458. - One *early* in therapsid evolution: Neotherapsida, Theriodontia, Eutheriodontia, and Cynodontia
  459. - One *closer to the mammalian lineage*: Eucynodontia, Probainognathia, and Mammaliamorpha
  460. - However, we still need to test for the significance of these trends using `overfitRR`. As for shifts, we use a threshold of p = 0.75 for significance.
  461. ```{r}
  462. # Generate alternative topologies
  463. resampleEQ1trend<-resampleTree(RREQ1$tree, nsim=100)
  464. # Recompile regressions for the new trees
  465. overfitEQ1trend<-overfitRR(RREQ1, y=EQnoOB, phylo.list=resampleEQ1trend, clus=cc)
  466. # Test for trend significance with new topologies
  467. overtrend1<-overfitST(RR=RREQ1, y=EQnoOB, oveRR=overfitEQ1trend, node=c(129,143), clus=cc)
  468. overtrend2<-overfitST(RR=RREQ1, y=EQnoOB, oveRR=overfitEQ1trend, node=c(130,144), clus=cc)
  469. overtrend3<-overfitST(RR=RREQ1, y=EQnoOB, oveRR=overfitEQ1trend, node=c(131,152), clus=cc)
  470. overtrend4<-overfitST(RR=RREQ1, y=EQnoOB, oveRR=overfitEQ1trend, node=c(132), clus=cc)
  471. # Display results
  472. ## For the whole tree
  473. overtrend1$trend.results$tree$phenotype
  474. ## For specific nodes
  475. overtrend1$trend.results$node$phenotype
  476. overtrend2$trend.results$node$phenotype
  477. overtrend3$trend.results$node$phenotype
  478. overtrend4$trend.results$node$phenotype
  479. ```
  480. The trend of EQ increase is **highly significant** (p > 0.95) for the whole tree and for all tested nodes.
  481. - Let's do a nicer plot of the phenotypic mean on the tree so we can visualize the trend better
  482. ```{r}
  483. # Extract ancestral states for the phenotypic mean (EQ)
  484. phenoplot<-ST$trend.data$phenotypeVStime
  485. phenonames<-rownames(phenoplot); phenonameshort<-phenonames[which(nchar(rownames(phenoplot))==3)]
  486. phenoanc<-phenoplot[which(nchar(rownames(phenoplot))==3),1]; names(phenoanc)<-phenonameshort
  487. # Plot them on the calibrated tree
  488. phenoMapCal<-contMap(RREQ1$tree, EQnoOB, method="user", anc.states=phenoanc, plot=FALSE)
  489. ## Previous palette
  490. plot(setMap(phenoMapCal,colors=colorRampPalette(pal)(10)),fsize=0.5,lwd=4,cex=c(0.5,0.3))
  491. ## Rainbow palette
  492. plot(setMap(phenoMapCal,colors=rev(brewer.pal(10, "Spectral"))),fsize=0.5,lwd=4,cex=c(0.5,0.3))
  493. # Plot them on the tree with all branch lengths equal (easier to visualize)
  494. phenoMapEqual<-contMap(nodetree, EQnoOB, method="user", anc.states=phenoanc, plot=FALSE)
  495. ## Previous palette
  496. plot(setMap(phenoMapEqual,colors=colorRampPalette(pal)(10)),fsize=0.5,lwd=4,cex=c(0.5,0.3))
  497. ## Rainbow palette
  498. plot(setMap(phenoMapEqual,colors=rev(brewer.pal(10, "Spectral"))),fsize=0.5,lwd=4,cex=c(0.5,0.3))
  499. ```
  500. - Visualize the significant phenotypic shifts on the time-calibrated tree with added geological time scale, using `strap` (Bell & Lloyd, 2015)
  501. ```{r}
  502. # Simple version
  503. geoscalePhylo(ttree, units=c("Period", "Epoch"), boxes="Epoch", cex.tip=0.4,
  504. cex.ts=0.8, tick.scale="Age", x.lim=c(270, 55))
  505. nodelabels(text="EQ+", node=c(129, 130, 131, 132, 143, 144, 152),
  506. adj=c(1.1, 1), frame="none", col="red2")
  507. # Version with stratigraphic ranges of individual taxa in the tree
  508. ages<-maindata[,5:6]; rownames(ages)<-maindata$Taxon
  509. geoscalePhylo(ttree, ages, units=c("Period", "Epoch"), boxes="Epoch", cex.tip=0.4,
  510. cex.ts=0.8, tick.scale="Age", x.lim=c(270, 55))
  511. nodelabels(text="EQ+", node=c(129, 130, 131, 132, 143, 144, 152),
  512. adj=c(1.1, 1), frame="none", col="red2")
  513. ```
  514. - Same with significant shifts in evolutionary rate
  515. ```{r}
  516. # Simple version
  517. geoscalePhylo(ttree, units=c("Period", "Epoch"), boxes="Epoch", cex.tip=0.4,
  518. cex.ts=0.8, tick.scale="Age", x.lim=c(270, 55))
  519. nodelabels(text="-", node=c(144:152, 155), cex=1.5,
  520. adj=c(1.1, 1), frame="none", col="steelblue2")
  521. # Version with stratigraphic ranges of individual taxa in the tree
  522. ages<-maindata[,5:6]; rownames(ages)<-maindata$Taxon
  523. geoscalePhylo(ttree, ages, units=c("Period", "Epoch"), boxes="Epoch", cex.tip=0.4,
  524. cex.ts=0.8, tick.scale="Age", x.lim=c(270, 55))
  525. nodelabels(text="-", node=c(144:152, 155), cex=1.5,
  526. adj=c(1.1, 1), frame="none", col="steelblue2")
  527. ```
  528. ### For EQ with OB
  529. - Prepare the data and tree
  530. ```{r}
  531. # Dataset
  532. EQ2<-logmain[,5]; names(EQ2)<-rownames(logmain)
  533. # Proportion of clusters
  534. cc <- 2/parallel::detectCores()
  535. # Compile RRphylo
  536. RREQ2<-RRphylo(tree=ttree, y=EQ2, clus=cc)
  537. # To visualize node numbers in the tree
  538. plotTree(nodetree, fsize=0.5, lwd=1, ftype="i")
  539. labelnodes(text=1:RREQ2$tree$Nnode,node=1:RREQ2$tree$Nnode+Ntip(RREQ2$tree),
  540. interactive=FALSE,circle.exp=0.4,cex=0.5)
  541. ```
  542. #### Let's test for significant shifts in evolutionary rates in the tree, using `search.shift`
  543. - In the whole tree (as a first step, to see if any main trends can be identified)
  544. ```{r}
  545. shiftsWholeR<-search.shift(RREQ2, status.type= "clade")
  546. shiftsWholeR$all.clades
  547. as.numeric(rownames(shiftsWholeR$all.clades))-length(ttree$tip.label) # actual node numbers as visualized in plotTree
  548. # Significant shifts
  549. posshift<-as.numeric(names(which(shiftsWholeR$all.clades[,2]>0.975)))
  550. posshift # Significant positive shifts
  551. negshift<-as.numeric(names(which(shiftsWholeR$all.clades[,2]<0.025)))
  552. negshift # Significant negative shifts
  553. # Plot them on the tree
  554. plotTree(nodetree, fsize=0.5, lwd=1, ftype="i")
  555. labelnodes(text=1:RREQ2$tree$Nnode,node=1:RREQ2$tree$Nnode+Ntip(RREQ2$tree),
  556. interactive=FALSE,circle.exp=0.4,cex=0.5)
  557. nodelabels(text="+", node=posshift, adj=c(1.5, 1), frame="none", col="red2")
  558. nodelabels(text="-", node=negshift, adj=c(1.5, 1), frame="none", col="steelblue2")
  559. ```
  560. **Two major series of shifts, both in Eucynodontia**: higher rates in Cynognathia (suggesting a selective pressure on EQ in that clade), and lower rates in Probainognathia (suggesting relaxed constraints, potentially associated with ancestral stabilizing selection).
  561. Results are very similar to those obtained with EQ with no OB.
  562. - Let's make a phylomorphospace for EQ with OB and body mass, with colors for these two clades:
  563. ```{r}
  564. # Data
  565. EQOBdat<-logmain[,c(3,5)]
  566. # Basic phylomorphospace, if you need it – remove the 'label="off"' part if you want taxa names, but it gets very crowded!
  567. par(mar = c(5.1, 4.1, 4.1, 2.1))
  568. phylomorphospace(ttree,EQOBdat,bty="l",label="off",xlab="log (Body mass) (g)",
  569. ylab="log (EQ) (with OB)",node.size=c(0,1))
  570. title(main="Phylomorphospace plot",font.main=3)
  571. # Basic tree with labelled clades
  572. plotTree(ttree,fsize=0.5,ftype="i")
  573. nodelabels(frame="circ",bg="white",cex=0.3)
  574. cladelabels(ttree,c("Probainognathia","Cynognathia"),c(144,179))
  575. # Define colors for the tree
  576. painted<-paintSubTree(ttree, 144, "Cynognathia")
  577. painted<-paintSubTree(painted, 179, "Probainognathia")
  578. # Plot it on the tree
  579. plot(painted, fsize=0.5, ftype="i")
  580. cladelabels(ttree, c("Probainognathia","Cynognathia"), c(144,179), offset=0.5)
  581. # Plot it on the phylomorphospace
  582. par(mar = c(5.1, 4.1, 4.1, 2.1))
  583. phylomorphospace(painted,EQOBdat,bty="l",label="off",xlab="log (Body mass) (g)",
  584. ylab="log (EQ) (with OB)",node.size=c(0,1.2),node.by.map=TRUE)
  585. title(main="Phylomorphospace plot",font.main=3)
  586. legend(x="topleft",legend=c("Cynognathia","Probainognathia"),
  587. pch=21,pt.cex=1.5,pt.bg=palette()[2:4],bty="n")
  588. # Plot it on the phenogram
  589. EQOB<-logmain[,5]; names(EQOB)<-rownames(logmain)
  590. par(mar = c(5.1, 4.1, 4.1, 2.1))
  591. phenogram(painted, EQOB, ,ftype="off", spread.labels=TRUE,
  592. spread.cost=c(1,0),fsize=0.7,
  593. xlab="Time (Ma)",ylab="log (EQ) (with OB)",cex.axis=0.8,
  594. axes=FALSE,las=1, lwd=1)
  595. axis(1,at=round(seq(0,max(nodeHeights(painted)),length.out=6),1),
  596. label=round(seq(max(nodeHeights(painted)),0,length.out=6),1),
  597. cex.axis=0.8)
  598. axis(2,las=1,cex.axis=0.8)
  599. grid()
  600. title(paste("Phenogram for EQ (with OB)"))
  601. legend(x="bottomright",legend=c("Probainognathia","Cynognathia"),
  602. pch=21,pt.cex=1.5,pt.bg=palette()[2:4],bty="n")
  603. ```
  604. As expected, Cynognathia show very high variation in a short amount of time, while the earliest nodes within Probainognathia seem to show limited variation over an extended time frame compared to more inclusive clades in the tree.
  605. Let's go a bit further... Same test, but for specific clades in the tree!
  606. We will be testing for shifts in the following clades:
  607. Biarmosuchia, Dinocephalia, Neotherapsida, Dicynodontia, Theriodontia, Gorgonopsia, Eutheriodontia, Therocephalia, Cynodontia, Eucynodontia, Cynognathia, Probainognathia, Mammaliamorpha, Mammaliaformes, Mammalia.
  608. - Specific shifts in those clades
  609. ```{r, warning=FALSE}
  610. # Node numbers
  611. nodes<-c(124,120,3,93,4,90,5,83,6,17,53,18,26,28,32)
  612. cornodes<-nodes+length(ttree$tip.label)
  613. # Look for significant shifts
  614. shiftsR<-search.shift(RREQ2, status.type= "clade", node=cornodes)
  615. shiftsR$single.clades
  616. which(shiftsR$single.clades[,2]>0.975); which(shiftsR$single.clades[,2]<0.025)
  617. posshift<-as.numeric(names(which(shiftsR$single.clades[,2]>0.975)))
  618. posshift # Significant positive shifts
  619. negshift<-as.numeric(names(which(shiftsR$single.clades[,2]<0.025)))
  620. negshift # Significant negative shifts
  621. # Plot them on the tree
  622. plotTree(nodetree, fsize=0.5, lwd=1, ftype="i")
  623. labelnodes(text=1:RREQ2$tree$Nnode,node=1:RREQ2$tree$Nnode+Ntip(RREQ2$tree),
  624. interactive=FALSE,circle.exp=0.4,cex=0.5)
  625. #nodelabels(text="+", node=posshift, adj=c(1.5, 1), frame="none", col="red2")
  626. nodelabels(text="-", node=negshift, adj=c(1.5, 1), frame="none", col="steelblue2")
  627. ```
  628. Results are congruent with those of the initial 'auto-recognize' search, but only clades that had already been identified as presenting a significant negative shift **in Probainognathia** are recovered as such.
  629. - We can plot the shifts on the tree
  630. ```{r}
  631. RRplotR<-plotRR(RREQ2, y=EQOB, multivariate = "rates")
  632. ## Recompile individual shifts for the plot
  633. posshift<-as.numeric(names(which(shiftsWholeR$all.clades[,2]>0.975)))
  634. negshift<-as.numeric(names(which(shiftsWholeR$all.clades[,2]<0.025)))
  635. EQ2shift<-search.shift(RREQ2, status.type= "clade", node=c(posshift, negshift))
  636. EQ2shift$single.clades<-as.data.frame(EQ2shift$single.clades)
  637. which(EQ2shift$single.clades[,2]>0.975); which(EQ2shift$single.clades[,2]<0.025)
  638. ## Values of EQ
  639. RRplotR$plotRRphen(variable=1, tree.args=list(no.margin=TRUE),
  640. colorbar.args=list(x=170, y=90, title.pos="bottom"))
  641. ## Rates and rate shifts on the same plot
  642. RRplotR$plotRRrates(variable=1, tree.args=list(no.margin=TRUE),
  643. colorbar.args=list(x=170, y=90, title.pos="bottom"))
  644. addShift(EQ2shift, symbols.args=list(lwd=2,fg=c(pos="red2",neg="steelblue2"),
  645. bg=scales::alpha(c(rep("red2",length(posshift)),rep("steelblue2",length(negshift))),0.3)))
  646. ## Only rate shifts
  647. plotShiftR<-plotShift(RREQ2, EQ2shift)
  648. plotShiftR$plotClades(tree.args=list(no.margin=TRUE),
  649. symbols.args=list(lwd=2,fg=c(pos="red2",neg="steelblue2"),
  650. bg=scales::alpha(c(rep("red2",length(posshift)),rep("steelblue2",length(negshift))),0.3)))
  651. ```
  652. - Finally, let's test for the significance of those shifts with jacknife/multiple permutation tests, using `overfitRR`. We use a threshold of 0.75 (3/4 of simulated trees recovered with the same shift) to assess a shift as significant.
  653. ```{r}
  654. # Generate alternative topologies
  655. resampleEQ2<-resampleTree(RREQ2$tree, node=c(posshift, negshift), nsim=1000)
  656. # Recompile regressions for the new trees
  657. overfitEQ2<-overfitRR(RREQ2, y=EQOB, phylo.list=resampleEQ2, clus=cc)
  658. # Test for shift significance with new topologies
  659. overshift1<-overfitSS(RR=RREQ2, oveRR=overfitEQ2, node=c(181,144))
  660. overshift2<-overfitSS(RR=RREQ2, oveRR=overfitEQ2, node=c(184,145))
  661. overshift3<-overfitSS(RR=RREQ2, oveRR=overfitEQ2, node=c(185,146))
  662. overshift4<-overfitSS(RR=RREQ2, oveRR=overfitEQ2, node=c(186,147))
  663. overshift5<-overfitSS(RR=RREQ2, oveRR=overfitEQ2, node=c(156,148))
  664. overshift6<-overfitSS(RR=RREQ2, oveRR=overfitEQ2, node=c(155,149))
  665. overshift7<-overfitSS(RR=RREQ2, oveRR=overfitEQ2, node=c(154,150))
  666. overshift8<-overfitSS(RR=RREQ2, oveRR=overfitEQ2, node=c(153,151))
  667. overshift9<-overfitSS(RR=RREQ2, oveRR=overfitEQ2, node=c(152,183))
  668. # Display the results
  669. overshift1$shift.results$clade$single.clades
  670. overshift2$shift.results$clade$single.clades
  671. overshift3$shift.results$clade$single.clades
  672. overshift4$shift.results$clade$single.clades
  673. overshift5$shift.results$clade$single.clades
  674. overshift6$shift.results$clade$single.clades
  675. overshift7$shift.results$clade$single.clades
  676. overshift8$shift.results$clade$single.clades
  677. overshift9$shift.results$clade$single.clades
  678. ```
  679. Nodes 18 through 30 (i.e. all clades within Probainognathia) all have significant negative shifts, whereas nodes within Cynodontia (positive shifts) do not.
  680. => **Only the rate shifts in Probainognathia and right outside Eucynodontia (negative, i.e. stabilizing selection of EQ) are recovered as significant.**
  681. Now that we have identified shifts in evolutionary rates, what about phenotypic trends (i.e. changes in the actual values of EQ through time)?
  682. #### Testing for significant phenotypic trends for EQ, using `search.trend`
  683. See the vignette and help section of the function, as well as Castiglione et al. (2019). We are testing for significant deviations from a Brownian Motion (BM) model in the value of our trait of interest (EQ).
  684. - For the whole tree
  685. ```{r}
  686. ST<-search.trend(RR=RREQ2, y=EQOB, nsim=1000)
  687. # Display the results
  688. rateres<-cbind(ST$rate.regression[1,1:4], NA); colnames(rateres)[5]<-"dev"
  689. phenres<-c(ST$phenotypic.regression[1,1:3], NA, ST$phenotypic.regression[1,4])
  690. names(phenres)[c(4,5)]<-c("spread", "dev")
  691. STres<-rbind(rateres, phenres)
  692. rownames(STres)<-c("rescaled absolute rate regression", "phenotypic regression")
  693. STres
  694. ```
  695. The absolute rate regression is not significant (p.random < 0.95), meaning the rates for the whole tree are not significantly different than expected under BM (makes sense, since we only identified localized shifts in the tree earlier). Accordingly, the 'spread' metric is very close to 1 (the expected value under BM).
  696. However, **the phenotypic regression is highly significant** (p.random = 1). The "dev" metric quantifies the deviation of the phenotypic mean of our trait from the root value, expressed in standard deviations of the distribution. Here, dev = 2, meaning that the value of EQ is almost twice as high as the standard deviation expected from BM. *There is a strong trend in our phenotype!* Since the slope is positive, this means we have a significant increase in EQ somewhere in the tree.
  697. - Let's test the same nodes we tested earlier for shifts
  698. ```{r}
  699. STclade1<-search.trend(RR=RREQ2, y=EQOB, node=c(250,246,129), nsim=1000)
  700. STclade2<-search.trend(RR=RREQ2, y=EQOB, node=c(219,130,216), nsim=1000)
  701. STclade3<-search.trend(RR=RREQ2, y=EQOB, node=c(131,209,179), nsim=1000)
  702. STclade4<-search.trend(RR=RREQ2, y=EQOB, node=c(143,132,144), nsim=1000)
  703. STclade5<-search.trend(RR=RREQ2, y=EQOB, node=c(152,154,158), nsim=1000)
  704. # Plot trends in EQ value on the tree
  705. plotTree(nodetree, fsize=0.5, lwd=1, ftype="i")
  706. labelnodes(text=1:RREQ2$tree$Nnode,node=1:RREQ2$tree$Nnode+Ntip(RREQ2$tree),
  707. interactive=FALSE,circle.exp=0.4,cex=0.5)
  708. nodelabels(text="EQ+", node=c(129, 130, 131, 132, 143, 144, 152, 154),
  709. adj=c(1.1, 1), frame="none", col="red2")
  710. # To get the values of EQ at nodes with significant shifts
  711. values2<-as.data.frame(cbind(as.numeric(rownames(RREQ2$aces)), expm1(RREQ2$aces)))
  712. Ivalues2<-values2 %>% filter (V1 %in% c(129:132, 143, 144, 152, 154))
  713. rownames(Ivalues2)<-c("Neotherapsida", "Theriodontia", "Eutheriodontia",
  714. "Cynodontia", "Eucynodontia", "Probainognathia",
  715. "Mammaliamorpha", "Mammaliaformes")
  716. colnames(Ivalues2)<-c("Node number", "EQ")
  717. Ivalues2
  718. ```
  719. Significant shifts in rates are the same as for our previous analyses: a positive shift for Cynognathia and a negative shift for Probainognathia.
  720. For absolute values of EQ, we have **two trends of significant increases**:
  721. - One *early* in therapsid evolution: Neotherapsida, Theriodontia, Eutheriodontia, and Cynodontia
  722. - One *closer to the mammalian lineage*: Eucynodontia, Probainognathia, Mammaliamorpha, and Mammaliaformes
  723. - However, we still need to test for the significance of these trends using `overfitRR`. As for shifts, we use a threshold of p = 0.75 for significance.
  724. ```{r}
  725. # Generate alternative topologies
  726. resampleEQ2trend<-resampleTree(RREQ2$tree, nsim=100)
  727. # Recompile regressions for the new trees
  728. overfitEQ2trend<-overfitRR(RREQ2, y=EQOB, phylo.list=resampleEQ2trend, clus=cc)
  729. # Test for trend significance with new topologies
  730. overtrend1<-overfitST(RR=RREQ2, y=EQOB, oveRR=overfitEQ2trend, node=c(129,154), clus=cc)
  731. overtrend2<-overfitST(RR=RREQ2, y=EQOB, oveRR=overfitEQ2trend, node=c(130,144), clus=cc)
  732. overtrend3<-overfitST(RR=RREQ2, y=EQOB, oveRR=overfitEQ2trend, node=c(131,152), clus=cc)
  733. overtrend4<-overfitST(RR=RREQ2, y=EQOB, oveRR=overfitEQ2trend, node=c(132,143), clus=cc)
  734. # Display results
  735. ## For the whole tree
  736. overtrend1$trend.results$tree$phenotype
  737. ## For specific nodes
  738. overtrend1$trend.results$node$phenotype
  739. overtrend2$trend.results$node$phenotype
  740. overtrend3$trend.results$node$phenotype
  741. overtrend4$trend.results$node$phenotype
  742. ```
  743. The trend of EQ increase is **highly significant** (p > 0.95) for the whole tree and for all tested nodes except Mammaliaformes, which makes the results identical to those of analyses with no OB.
  744. - Let's do a nicer plot of the phenotypic mean on the tree so we can visualize the trend better
  745. ```{r}
  746. # Extract ancestral states for the phenotypic mean (EQ)
  747. phenoplot<-ST$trend.data$phenotypeVStime
  748. phenonames<-rownames(phenoplot); phenonameshort<-phenonames[which(nchar(rownames(phenoplot))==3)]
  749. phenoanc<-phenoplot[which(nchar(rownames(phenoplot))==3),1]; names(phenoanc)<-phenonameshort
  750. # Plot them on the calibrated tree
  751. phenoMapCal<-contMap(RREQ1$tree, EQOB, method="user", anc.states=phenoanc, plot=FALSE)
  752. ## Previous palette
  753. plot(setMap(phenoMapCal,colors=colorRampPalette(pal)(10)),fsize=0.5,lwd=4,cex=c(0.5,0.3))
  754. ## Rainbow palette
  755. plot(setMap(phenoMapCal,colors=rev(brewer.pal(10, "Spectral"))),fsize=0.5,lwd=4,cex=c(0.5,0.3))
  756. # Plot them on the tree with all branch lengths equal (easier to visualize)
  757. phenoMapEqual<-contMap(nodetree, EQOB, method="user", anc.states=phenoanc, plot=FALSE)
  758. ## Previous palette
  759. plot(setMap(phenoMapEqual,colors=colorRampPalette(pal)(10)),fsize=0.5,lwd=4,cex=c(0.5,0.3))
  760. ## Rainbow palette
  761. plot(setMap(phenoMapEqual,colors=rev(brewer.pal(10, "Spectral"))),fsize=0.5,lwd=4,cex=c(0.5,0.3))
  762. ```
  763. - Same with evolutionary rates
  764. ```{r}
  765. # Extract ancestral states for evolutionary rates
  766. rateplot<-ST$trend.data$rateVStime
  767. ratenames<-rownames(rateplot)
  768. ratenameshort<-ratenames[which(nchar(rownames(rateplot))==3)]
  769. rateanc<-rateplot[which(nchar(rownames(rateplot))==3),1]
  770. names(rateanc)<-ratenameshort
  771. # Plot them on the calibrated tree
  772. rateMapCal<-contMap(RREQ1$tree, EQOB, method="user", anc.states=rateanc, plot=FALSE)
  773. ## Previous palette
  774. plot(setMap(rateMapCal,colors=colorRampPalette(pal)(10)),
  775. fsize=0.5,lwd=4,cex=c(0.5,0.3))
  776. ## Rainbow palette
  777. plot(setMap(rateMapCal,colors=rev(brewer.pal(10,"Spectral"))),
  778. fsize=0.5,lwd=4,cex=c(0.5,0.3))
  779. # Plot them on the tree with all branch lengths equal (easier to visualize)
  780. rateMapEqual<-contMap(nodetree, EQOB, method="user", anc.states=rateanc, plot=FALSE)
  781. ## Previous palette
  782. plot(setMap(rateMapEqual,colors=colorRampPalette(pal)(10)),
  783. fsize=0.5,lwd=4,cex=c(0.5,0.3))
  784. ## Rainbow palette
  785. plot(setMap(rateMapEqual,colors=rev(brewer.pal(10,"Spectral"))),
  786. fsize=0.5,lwd=4,cex=c(0.5,0.3))
  787. ```
  788. - Visualize the phenotypic shifts on the time-calibrated tree with added geological time scale, using `strap` (Bell & Lloyd, 2015)
  789. ```{r}
  790. # Simple version
  791. geoscalePhylo(ttree, units=c("Period", "Epoch"), boxes="Epoch", cex.tip=0.4,
  792. cex.ts=0.8, tick.scale="Age", x.lim=c(270, 55))
  793. nodelabels(text="EQ+", node=c(129, 130, 131, 132, 143, 144, 152),
  794. adj=c(1.1, 1), frame="none", col="red2")
  795. # Version with stratigraphic ranges of individual taxa in the tree
  796. ages<-maindata[,5:6]; rownames(ages)<-maindata$Taxon
  797. geoscalePhylo(ttree, ages, units=c("Period", "Epoch"), boxes="Epoch", cex.tip=0.4,
  798. cex.ts=0.8, tick.scale="Age", x.lim=c(270, 55))
  799. nodelabels(text="EQ+", node=c(129, 130, 131, 132, 143, 144, 152),
  800. adj=c(1.1, 1), frame="none", col="red2")
  801. ```
  802. - Same with significant shifts in evolutionary rate
  803. ```{r}
  804. # Simple version
  805. geoscalePhylo(ttree, units=c("Period", "Epoch"), boxes="Epoch", cex.tip=0.4,
  806. cex.ts=0.8, tick.scale="Age", x.lim=c(270, 55))
  807. nodelabels(text="-", node=c(142, 144:146, 149:152, 154, 155), cex=1.5,
  808. adj=c(1.1, 1), frame="none", col="steelblue2")
  809. # Version with stratigraphic ranges of individual taxa in the tree
  810. ages<-maindata[,5:6]; rownames(ages)<-maindata$Taxon
  811. geoscalePhylo(ttree, ages, units=c("Period", "Epoch"), boxes="Epoch", cex.tip=0.4,
  812. cex.ts=0.8, tick.scale="Age", x.lim=c(270, 55))
  813. nodelabels(text="-", node=c(142, 144:146, 149:152, 154, 155), cex=1.5,
  814. adj=c(1.1, 1), frame="none", col="steelblue2")
  815. ```
  816. ## New analyses of evolutionary shifts and regimes in an OU framework
  817. ### Using `phylolm` (Ho & Ané, 2014)
  818. ```{r, warning=FALSE}
  819. ## For EQ with no OB
  820. result<-OUshifts(EQ1, ttree, method="mbic", nmax=ttree$Nnode)
  821. result$mean; result$pshift; result$shift
  822. par(mar=c(0,0,0,0)); plot.OUshifts(result, cex=0.5, show.data=FALSE)
  823. ## For EQ with OB
  824. result2<-OUshifts(EQ2, ttree, method="mbic", nmax=ttree$Nnode)
  825. result2$mean; result2$pshift; result2$shift
  826. par(mar=c(0,0,0,0)); plot.OUshifts(result2, cex=0.5, show.data=FALSE)
  827. ## Tree with edge labels to visualize numbers
  828. plotTree(nodetree, fsize=0.5, lwd=1, ftype="i")
  829. edgelabels(frame="circle", cex=0.5, bg="white")
  830. ```
  831. For both EQ (with and without OB), the results are identical: four shifts recovered, only one of which is at an internal branch: branch 29, which leads to node 30: Mammalia + *Cifelliodon* + *Hadrocodium*. It is a positive shift.
  832. The other three shifts are recovered at the following terminal taxa (one therocephalian and two non-eucynodont cynodonts):
  833. - *Galesaurus planiceps* 1 (positive shift);
  834. - *Thrinaxodon liorhinus* 3 (negative shift);
  835. - *Tetracynodon darti* 1 (negative shift).
  836. This seems to be congruent with our previous results obtained with `RRphylo`: the only significant shifts occur near the origin of mammals, with more inclusive clades not affected by the PTME and not showing any conspicuous patterns. We do not, however, recover any shifts near the base of the tree.
  837. ### Using `slouch` (Kopperud et al., 2024)
  838. - Preparing the nodes in the tree
  839. ```{r}
  840. # Defining regimes in the tree (different clades of interest – same as those used with `RRphylo`)
  841. nodenames<-c(rep("Therapsida", 2), "Neotherapsida", "Theriodontia",
  842. "Eutheriodontia", rep("Cynodontia", 11), "Eucynodontia",
  843. rep("Probainognathia", 8), rep("Mammaliamorpha", 2),
  844. rep("Mammaliaformes", 4), rep("Mammalia", 10),
  845. rep("Mammaliamorpha", 5), rep("Probainognathia", 6),
  846. rep("Cynognathia", 22), rep("Cynodontia", 8),
  847. rep("Therocephalia",7), rep("Gorgonopsia", 3),
  848. rep("Dicynodontia", 27), rep("Dinocephalia", 4),
  849. rep("Biarmosuchia", 2))
  850. ttree$node.label<-nodenames # Add them as node names in the tree
  851. # Map them on the tree
  852. tipnames<-c(rep("Biarmosuchia", 3), rep("Dinocephalia", 2),
  853. rep("Dicynodontia", 18), "Gorgonopsia", rep("Therocephalia", 8),
  854. rep("Cynodontia", 13), rep("Cynognathia", 13),
  855. rep("Probainognathia", 8), rep("Mammaliamorpha", 4),
  856. rep("Mammaliaformes", 4), rep("Mammalia", 11),
  857. rep("Dicynodontia", 4), rep("Cynognathia", 7), rep("Dinocephalia", 3),
  858. rep("Dicynodontia", 6), rep("Gorgonopsia", 3), rep("Cynodontia", 6),
  859. rep("Cynognathia", 3), rep("Probainognathia", 6),
  860. rep("Mammaliamorpha", 3))
  861. regimes<-c(as.factor(tipnames), as.factor(nodenames))
  862. levels(regimes)<-c("Therapsida", "Biarmosuchia", "Dinocephalia", "Neotherapsida", "Dicynodontia", "Theriodontia", "Gorgonopsia", "Eutheriodontia", "Therocephalia", "Cynodontia", "Eucynodontia", "Cynognathia", "Probainognathia", "Mammaliamorpha", "Mammaliaformes", "Mammalia")
  863. ```
  864. - Color palette for different regimes
  865. ```{r}
  866. # Custom palette with 16 colors
  867. rainbowpal<-c(brewer.pal(9,"Set1"), brewer.pal(7,"Set2"))
  868. show_col(rainbowpal) # Looks good
  869. names(rainbowpal)<-levels(regimes)
  870. edge_regimes<-factor(regimes[ttree$edge[,2]])
  871. par(mar=c(1,1,1,1)+0.1)
  872. plot(ttree,
  873. edge.color = rainbowpal[edge_regimes],
  874. edge.width = 3, cex = 0.6) # Groups are correctly separated by color
  875. ```
  876. - Build the model with those regimes
  877. ```{r}
  878. slouchmod <- slouch.fit(phy = ttree,
  879. species = logmain$Taxon,
  880. response = logmain$EQ_noOB,
  881. fixed.fact = as.factor(tipnames))
  882. summary(slouchmod)
  883. # BM models for comparison
  884. ## With no regimes (simplest)
  885. BMmod <- brown.fit(phy = ttree,
  886. species = logmain$Taxon,
  887. response = logmain$EQ_noOB)
  888. summary(BMmod)
  889. ## With regimes (trend)
  890. BMmodtrend <- brown.fit(phy = ttree,
  891. species = logmain$Taxon,
  892. response = logmain$EQ_noOB,
  893. fixed.fact = as.factor(tipnames))
  894. summary(BMmodtrend)
  895. slouchmod$modfit$AICc; BMmod$modfit$AICc; BMmodtrend$modfit$AICc
  896. # OU is a better fit than BM here
  897. slouchmod$beta_primary$coefficients # optima for each node of interest
  898. ```
  899. ## References
  900. - Bell, M.A., Lloyd, G.T., 2015. strap: an R package for plotting phylogenies against stratigraphy and assessing their stratigraphic congruence. *Palaeontology* 58, 379–389. <https://doi.org/10.1111/pala.12142>
  901. - Castiglione, S., Serio, C., Mondanaro, A., di Febbraro, M., Profico, A., Girardi, G., Raia, P., 2019. Simultaneous detection of macroevolutionary patterns in phenotypic means and rate of change with and within phylogenetic trees including extinct species. *PLOS ONE* 14, e0210101. <https://doi.org/10.1371/journal.pone.0210101>
  902. - Castiglione, S., Tesone, G., Piccolo, M., Melchionna, M., Mondanaro, A., Serio, C., di Febbraro, M., Raia, P., 2018. A new method for testing evolutionary rate variation and shifts in phenotypic evolution. *Methods in Ecology and Evolution* 9, 974–983. <https://doi.org/10.1111/2041-210X.12954>
  903. - Ho, L.S.T., Ané, C., 2014. A linear-time algorithm for Gaussian and non-Gaussian trait evolution models. *Systematic Biology* 63, 397–408. <https://doi.org/10.1093/sysbio/syu005>
  904. - Goolsby, E.W., Bruggeman, J., Ané, C., 2017. Rphylopars: fast multivariate phylogenetic comparative methods for missing data and within-species variation. *Methods in Ecology and Evolution* 8, 22–27. <https://doi.org/10.1111/2041-210X.12612>
  905. - Kopperud, B.T., Pienaar, J., Voje, K.L., Orzack, S.H., Hansen, T.F., Grabowski, M., 2024. *slouch: Stochastic Linear Ornstein-Uhlenbeck Comparative Hypotheses.* R package, available at: <https://cran.r-project.org/web/packages/slouch/index.html>
  906. - Kratsch, C., McHardy, A.C., 2014. RidgeRace: ridge regression for continuous ancestral character estimation on phylogenetic trees. *Bioinformatics* 30, i527–i533. <https://doi.org/10.1093/bioinformatics/btu477>
  907. - Pennell, M.W., Eastman, J.M., Slater, G.J., Brown, J.W., Uyeda, J.C., FitzJohn, R.G., Alfaro, M.E., Harmon, L.J., 2014. geiger v2.0: an expanded suite of methods for fitting macroevolutionary models to phylogenetic trees. *Bioinformatics* 30, 2216–2218. <https://doi.org/10.1093/bioinformatics/btu181>
  908. - Revell, L.J., 2024. phytools 2.0: an updated R ecosystem for phylogenetic comparative methods (and other things). *PeerJ* 12, e16505. <https://doi.org/10.7717/peerj.16505>

Script_Benoitetal_2026.Rmd at commit 521ae20, no license · at the source

Overview

Authors: Julien Benoit1, Lucas J. Legendre2, Ricardo Araújo3, Vincent Fernandez4, Adam Midzuk1, Claire Browning5,6, Fernando Abdala1,7, Jennifer Botha1,8, Kenneth D. Angielczyk1,9
  1. Evolutionary Studies Institute, School of Geosciences, University of the Witwatersrand,Private Bag 3, Johannesburg, 2050 WITS South Africa
  2. Department of Earth and Planetary Sciences, The University of Texas at Austin,2305 Speedway Stop C1160, Austin, TX 78712 USA
  3. Centro de Recursos Naturais e Ambiente (CERENA), Instituto Superior Técnico, Universidade de Lisboa,Lisboa, Portugal
  4. European Synchrotron Radiation Facility,71 rue des Martyrs, Grenoble, France
  5. Iziko Museums of South Africa,Cape Town, 8000 South Africa
  6. Department of Geological Sciences, University Avenue South, University of Cape Town Rondebosch,13 University Avenue South, Cape Town, South Africa
  7. Unidad Ejecutora Lillo (CONICET-Fundación Miguel Lillo),Miguel Lillo 251, Tucumán San Miguel de Tucumán, Argentina
  8. GENUS: DSTI-NRF Centre of Excellence in Palaeosciences, University of the Witwatersrand,Johannesburg, South Africa
  9. Negaunee Integrative Research Center, Field Museum of Natural History,1400 South DuSable Lake Shore Drive, Chicago, IL 60605 USA
Journal: Scientific reports, volume 16, issue 1, article 22211
Dates: received 27 February 2026; accepted 11 May 2026; published online 15 May 2026
Type: Research article · Language: English
License: CC BY-NC-ND
Identifiers: DOI 10.1038/s41598-026-53133-y · PMID 42141026 · PMCID PMC13369837 · OpenAlex W7161248481
Open access: gold, a free copy (OpenAlex)
Status: code verified
Methods: Statistics
Keywords: Biological crisis, Encephalization, Brain, Therapsida, Permian-Triassic extinction, Ecology, Evolution, Neuroscience
MeSH: Biological Evolution*, Brain*, Extinction, Biological*, Fossils*, Mammals*, Animals, Phylogeny (* major topic)
Topic: Protist diversity and phylogeny (Molecular Biology, Biochemistry, Genetics and Molecular Biology), according to OpenAlex
Funding: DSTI-NRF African Origins Platform (AOP210218587003, AOP240418214774, AOP240326210961); Consejo Nacional de Investigaciones Científicas y Técnicas; GENUS: DSTI-NRF Centre of Excellence in Palaeosciences
Citations: not cited yet (Europe PMC); 119 references in the paper

Abstract

The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.

Repository

Its files are read in the Code ↔ Paper reader above, with 6 matches between paragraphs and lines of code.

LucasLegendre/synapsid_EQ

License: none: the authors keep all their rights
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Commit: 521ae2067152c788ee0540a74c053e26702a176c, 22 April 2026
Languages: R (2)
Size: 7 files, 2 scripts
Software Heritage: not archived
Found in: the text, “Phylogenetic comparative analyses”
Holds: README, CITATION.cff, 2 notebooks
Not found: license file, environment file, tests, continuous integration, documentation
Tools: nlme (2 files), tidyverse (2 files)
Availability: 1 check, the latest on 28 September 2026: the link answers
  • 28 September 2026: the link answers
3 files

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 2 scripts, each with its path and the digest of its content;
  • 6 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

No dataset and no data link were found in the paper.

Code and data availability statement

The paper has a code and data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

  • no repository, dataset or request procedure was recognized in it

Read it in the paper: doi.org/10.1038/s41598-026-53133-y.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 28 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 9 authors, 8 keywords, 7 MeSH terms, 3 funders, 113 references.

Cite

This paper

Benoit, J., Legendre, L. J., Araújo, R., Fernandez, V., Midzuk, A., Browning, C., Abdala, F., Botha, J., & Angielczyk, K. D. (2026). Evolutionary stasis in synapsid encephalization during the end-permian mass extinction. Scientific reports, 16(1), 22211. https://doi.org/10.1038/s41598-026-53133-y

BibTeX

@article{benoit2026evolutionary,
author = {Benoit, Julien and Legendre, Lucas J. and Araújo, Ricardo and Fernandez, Vincent and Midzuk, Adam and Browning, Claire and Abdala, Fernando and Botha, Jennifer and Angielczyk, Kenneth D.},
title = {{Evolutionary stasis in synapsid encephalization during the end-permian mass extinction}},
journal = {Scientific reports},
year = {2026},
month = may,
volume = {16},
number = {1},
pages = {22211},
publisher = {Nature Publishing Group},
issn = {2045-2322},
doi = {10.1038/s41598-026-53133-y},
url = {https://doi.org/10.1038/s41598-026-53133-y},
pmid = {42141026},
pmcid = {PMC13369837}
}

RIS

TY - JOUR
AU - Benoit, Julien
AU - Legendre, Lucas J.
AU - Araújo, Ricardo
AU - Fernandez, Vincent
AU - Midzuk, Adam
AU - Browning, Claire
AU - Abdala, Fernando
AU - Botha, Jennifer
AU - Angielczyk, Kenneth D.
TI - Evolutionary stasis in synapsid encephalization during the end-permian mass extinction
T2 - Scientific reports
J2 - Sci Rep
PY - 2026
DA - 2026/05/15
VL - 16
IS - 1
SP - 22211
SN - 2045-2322
PB - Nature Publishing Group
DO - 10.1038/s41598-026-53133-y
UR - https://doi.org/10.1038/s41598-026-53133-y
LA - en
ER -

CSL-JSON

{
"id": "10.1038/s41598-026-53133-y",
"type": "article-journal",
"title": "Evolutionary stasis in synapsid encephalization during the end-permian mass extinction",
"container-title": "Scientific reports",
"author": [
{
"family": "Benoit",
"given": "Julien"
},
{
"family": "Legendre",
"given": "Lucas J."
},
{
"family": "Araújo",
"given": "Ricardo"
},
{
"family": "Fernandez",
"given": "Vincent"
},
{
"family": "Midzuk",
"given": "Adam"
},
{
"family": "Browning",
"given": "Claire"
},
{
"family": "Abdala",
"given": "Fernando"
},
{
"family": "Botha",
"given": "Jennifer"
},
{
"family": "Angielczyk",
"given": "Kenneth D."
}
],
"container-title-short": "Sci Rep",
"volume": "16",
"issue": "1",
"page": "22211",
"DOI": "10.1038/s41598-026-53133-y",
"PMID": "42141026",
"PMCID": "PMC13369837",
"ISSN": "2045-2322",
"publisher": "Nature Publishing Group",
"URL": "https://doi.org/10.1038/s41598-026-53133-y",
"language": "en",
"issued": {
"date-parts": [
[
2026,
5,
15
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1371/journal.pbio.3003771 [code]
Bipedalism and brain expansion explain human handedness.
Journal: PLoS biology
In common: 3 references
[2] doi:10.3390/biology15161373
Elevational Variation in Organ Size and Brain-Viscera Energetic Trade-Offs in the Minshan Toad (&lt;i&gt;Bufo minshanicus&lt;/i&gt;).
Journal: Biology
In common: 3 references
[3] doi:10.1038/s41586-026-10809-9 [code]
Exceptional brain and ecological diversity in the earliest snakes.
Journal: Nature
In common: 3 references
[4] doi:10.1093/nar/gkag544 [code]
Single-base resolution atlas reveals moderate conservation and regulatory diversity of m6A modifications across mammals.
Journal: Nucleic acids research
In common: nlme, tidyverse, 1 reference
[5] doi:10.3897/bdj.14.e191439 [code]
Assessing patterns of extinction risk amongst mammal species in Nigeria: A comparative analysis of human impact.
Journal: Biodiversity data journal
In common: tidyverse, 2 references
[6] doi:10.1038/s41514-026-00439-w [code]
Effects of a three-month exercise programme on cognition, mood and neurogenesis: the NeuroFit randomised controlled trial.
Journal: npj aging
In common: nlme, tidyverse
[7] doi:10.1038/s42003-026-10957-8 [code]
Brain defence by the extracellular matrix protein Cochlin.
Journal: Communications biology
In common: nlme, tidyverse
[8] doi:10.1126/sciadv.aec9291 [code]
Computational mechanisms of perception in autism revealed using games inspired by rodent operant tasks.
Journal: Science advances
In common: nlme, tidyverse
[9] doi:10.1093/braincomms/fcag343 [code]
Long-term brain volume trajectories and lifestyle associations in cognitively normal adults: the BRAIN-STRIDE study.
Journal: Brain communications
In common: nlme, tidyverse
[10] doi:10.1162/imag.a.1337 [code]
Data quality biases normative models derived from fetal brain MRI.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: nlme, tidyverse

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.