library(ape)
library(phytools)
library(geiger)
library(mvMORPH)Roteiro prático — Modelos de evolução contínua
Aula 4 · Métodos Filogenéticos Comparativos
0. Pacotes
1. A verossimilhança do BM, à mão
Sob BM, os valores das pontas seguem uma normal multivariada com média z0 (a raiz) e covariância σ²C. Com o atributo A da árvore-guia:
guia <- read.tree(text = "(((sp1:0.18,sp2:0.18):1.06,(sp3:0.02,sp4:0.02):1.22):0.20,(sp5:1.37,(sp6:0.23,sp7:0.23):1.14):0.07);")
A <- setNames(c(2.51, 2.80, 2.23, 2.32, 1.64, 1.18, 0.58), paste0("sp", 1:7))
C <- vcv(guia)[names(A), names(A)]
Ci <- solve(C); n <- length(A); um <- rep(1, n)
z0 <- as.numeric((um %*% Ci %*% A) / (um %*% Ci %*% um)) # a raiz (GLS)
logL <- function(sig2) -0.5 * (n * log(2 * pi) + n * log(sig2) + log(det(C)) +
as.numeric(t(A - z0) %*% Ci %*% (A - z0)) / sig2)
s2 <- seq(0.05, 1.2, length.out = 200)
plot(s2, sapply(s2, logL), type = "l", xlab = expression(sigma^2), ylab = "log L")
s2_mv <- as.numeric(t(A - z0) %*% Ci %*% (A - z0)) / n # o máximo, analítico
abline(v = s2_mv, lty = 2)
c(z0 = z0, sigma2 = s2_mv, logL = logL(s2_mv)) z0 sigma2 logL
1.8293475 0.3560036 -4.4493542
O fitContinuous() chega aos mesmos valores:
bm_A <- fitContinuous(guia, A, model = "BM")
c(z0 = bm_A$opt$z0, sigma2 = bm_A$opt$sigsq, logL = bm_A$opt$lnL) z0 sigma2 logL
1.8293475 0.3560036 -4.4493542
2. Simulando os outros modelos
O mesmo fastBM() simula um OU (atração α para o ótimo θ) e uma tendência (deslocamento μ por unidade de tempo). Os fenogramas mostram a diferença.
set.seed(505)
arv26 <- pbtree(n = 26, scale = 10)
par(mfrow = c(2, 2))
phenogram(arv26, fastBM(arv26, sig2 = 0.1, internal = TRUE), ftype = "off", main = "BM")
phenogram(arv26, fastBM(arv26, a = 0, theta = 3, alpha = 0.2, sig2 = 0.1, internal = TRUE),
ftype = "off", main = "OU, α = 0,2")
phenogram(arv26, fastBM(arv26, a = 0, theta = 3, alpha = 0.8, sig2 = 0.1, internal = TRUE),
ftype = "off", main = "OU, α = 0,8")
phenogram(arv26, fastBM(arv26, a = 0, mu = 0.3, sig2 = 0.1, internal = TRUE),
ftype = "off", main = "tendência, μ = 0,3")
Com α maior, as linhagens ficam mais presas ao redor de θ. A tendência desloca a média, mas numa árvore só de espécies atuais isso não se distingue do BM (os slides mostram por quê): é preciso fósseis.
3. Comparando modelos nos Anolis
O tamanho do corpo (SVL, já em log natural no anole.data):
data(anoletree, anole.data)
arv <- as.phylo(anoletree)
svl <- setNames(anole.data$SVL, rownames(anole.data))
set.seed(509) # o fitContinuous sorteia pontos de partida
ajustes <- lapply(c(BM = "BM", OU = "OU", EB = "EB"), \(m) fitContinuous(arv, svl, model = m))
tab <- data.frame(modelo = names(ajustes),
k = sapply(ajustes, \(f) f$opt$k),
logL = sapply(ajustes, \(f) f$opt$lnL),
AICc = sapply(ajustes, \(f) f$opt$aicc))
tab$dAICc <- tab$AICc - min(tab$AICc)
tab$peso <- exp(-0.5 * tab$dAICc) / sum(exp(-0.5 * tab$dAICc))
tab modelo k logL AICc dAICc peso
BM BM 2 5.256121 -6.360343 0.5673841 0.3743952
OU OU 3 5.263849 -4.220005 2.7077220 0.1283990
EB EB 3 6.617710 -6.927727 0.0000000 0.4972058
Nenhum modelo se destaca: os pesos dizem quanto cada modelo é apoiado em relação aos outros, não se ele é bom. O α do OU deve ser lido pela meia-vida, comparada com a altura da árvore:
alfa <- ajustes$OU$opt$alpha
c(alfa = alfa, meia_vida = log(2) / alfa, altura = max(nodeHeights(arv))) alfa meia_vida altura
6.162154e-14 1.124846e+13 6.000000e+00
Aqui α ficou praticamente zero e a meia-vida, muito maior que a árvore: o OU “colapsou” no BM, e por isso tem o mesmo log L com um parâmetro a mais.
4. O modelo escolhido é adequado?
O menor AICc não garante que o modelo descreva bem os dados. Uma checagem simples, no espírito do Arbutus: escolher uma estatística, calculá-la nos dados e em muitos conjuntos simulados pelo modelo ajustado, e comparar. A estatística aqui é a inclinação entre o tamanho dos contrastes e a idade do nó: sob BM, ela deve ser próxima de zero.
inclinacao_idade <- function(x, arvore) {
ct <- pic(x, arvore)
idade <- branching.times(arvore)[names(ct)]
coef(lm(abs(ct) ~ idade))[2]
}
obs <- inclinacao_idade(svl, arv)
set.seed(510)
sim <- replicate(500, inclinacao_idade(fastBM(arv, a = ajustes$BM$opt$z0,
sig2 = ajustes$BM$opt$sigsq), arv))
hist(sim, main = "", xlab = "inclinação |contraste| × idade do nó (simulado sob BM)")
abline(v = obs, col = "firebrick", lwd = 2)
obs idade
0.01589652
mean(abs(sim) >= abs(obs)) # valor-p aproximado[1] 0.008
A inclinação observada fica fora do que o BM ajustado produz: contrastes maiores nos nós antigos, o padrão de um early burst, de acordo com o peso do EB na tabela. O BM, sozinho, não descreve bem estes dados.
5. Taxas diferentes em partes da árvore
O Brownie (brownie.lite()) compara um BM com uma taxa só e um com uma taxa por regime mapeado. Nos Centrarchidae, o regime é o modo de alimentação:
data(sunfish.tree, sunfish.data)
abertura <- setNames(sunfish.data$gape.width, rownames(sunfish.data))
brownie.lite(sunfish.tree, abertura)ML single-rate model:
s^2 se a k logL
value 0.1139856 0.03045653 0.0352028 2 29.74964
ML multi-rate model:
s^2(non) se(non) s^2(pisc) se(pisc) a k logL
value 0.1640474 0.05869327 0.0551884 0.02290415 0.0512651 3 31.45567
P-value (based on X^2): 0.0647216
R thinks it has found the ML solution.
6. Ótimos diferentes: o OU com vários regimes
Nos Anolis, os ecomorfos estão mapeados na árvore. O mvMORPH ajusta um OU com um ótimo por ecomorfo (OUM). Reescalar a árvore para altura 1 ajuda a otimização (e não muda a comparação entre modelos).
arv_eco <- anoletree
h <- max(nodeHeights(arv_eco))
arv_eco$edge.length <- arv_eco$edge.length / h
arv_eco$maps <- lapply(arv_eco$maps, \(m) m / h)
arv_eco$mapped.edge <- arv_eco$mapped.edge / h
svl_eco <- svl[arv_eco$tip.label]
r <- list(BM1 = mvBM(arv_eco, svl_eco, model = "BM1", echo = FALSE, diagnostic = FALSE),
BMM = mvBM(arv_eco, svl_eco, model = "BMM", echo = FALSE, diagnostic = FALSE),
OU1 = mvOU(arv_eco, svl_eco, model = "OU1", echo = FALSE, diagnostic = FALSE,
param = list(root = FALSE)),
OUM = mvOU(arv_eco, svl_eco, model = "OUM", echo = FALSE, diagnostic = FALSE,
param = list(root = FALSE)))
sapply(r, \(f) c(logL = f$LogLik, AICc = f$AICc)) BM1 BMM OU1 OUM
logL 5.256121 26.24581 2.8640374 40.89968
AICc -6.360343 -36.97811 0.5796176 -63.82675
Os ótimos estimados (θ) de cada ecomorfo e a meia-vida, em unidades da altura da árvore:
r$OUM$theta
CG 5.055558
GB 3.697700
TC 4.055975
TG 4.087267
Tr 3.859119
Tw 3.862150
log(2) / r$OUM$alpha
0.07284066
Uma meia-vida curta quer dizer atração forte: cuidado ao interpretar α (Ho & Ané 2014; Cooper et al. 2016; e a resposta de Grabowski et al. 2023, nos slides).
Resumo
- A verossimilhança do BM sai de σ²C;
fitContinuous()estima σ² e a raiz. - BM, OU e EB se comparam por AICc e pesos; leia α pela meia-vida.
- O modelo escolhido precisa ser checado (adequação).
brownie.lite()testa taxas por regime;mvMORPHajusta ótimos por regime.
O roteiro da edição de 2020 tem o bayou (OU multi-regime bayesiano), e o roteiro da Aula 2 traz outra comparação de modelos e a adequação com o pacote arbutus.