# Instalar remotes si no está instalado
if (!requireNamespace("remotes", quietly = TRUE)) {
install.packages("remotes")
}
# Instalar TreePar
::install_github("tanja819/TreePar") remotes
Introducción al proceso de diversificación
Presentación: Introducción al proceso de diversificación
Haz clic en la imagen para ver el PDF de la presentación
Introducción a los métodos de diversificación
En este módulo exploraremos dos enfoques para el análisis de tasas de diversificación:
TreePar: Implementado en R, este paquete permite detectar cambios en las tasas de especiación y extinción en árboles filogenéticos a partir de datos temporales. Nos enfocaremos en la función
bd.shifts.optim
, que optimiza modelos de nacimiento y muerte con cambios de tasa.RevBayes: Utilizaremos el enfoque bayesiano para modelar la variación en tasas de diversificación a lo largo del tiempo, siguiendo el tutorial oficial Simple Diversification Rate Model.
Instalación de Paquetes en R
Para realizar los análisis de diversificación, es necesario instalar y cargar varios paquetes en R. Si no los tienes instalados, puedes ejecutar el siguiente código:
# Instalar tidyverse si no está instalado
if (!requireNamespace("tidyverse", quietly = TRUE)) {
install.packages("tidyverse", dependencies = TRUE)
}
# Instalar ape si no está instalado
if (!requireNamespace("ape", quietly = TRUE)) {
install.packages("ape", dependencies = TRUE)
}
# Instalar BiocManager si no está instalado
if (!requireNamespace("BiocManager", quietly = TRUE)) {
install.packages("BiocManager", dependencies = TRUE)
}
# Instalar ggtree y treeio si no están instalados
if (!requireNamespace("ggtree", quietly = TRUE) || !requireNamespace("treeio", quietly = TRUE)) {
::install(c("ggtree", "treeio"))
BiocManager
}
# Instalar subplex si no está instalado
if (!requireNamespace("subplex", quietly = TRUE)) {
install.packages("subplex", dependencies = TRUE)
}
# Instalar TreeSim si no está instalado
if (!requireNamespace("TreeSim", quietly = TRUE)) {
install.packages("TreeSim", dependencies = TRUE)
}
# Instalar deSolve si no está instalado
if (!requireNamespace("deSolve", quietly = TRUE)) {
install.packages("deSolve", dependencies = TRUE)
}
# Instalar phytools si no está instalado
if (!requireNamespace("phytools", quietly = TRUE)) {
install.packages("phytools")
}
# Instalar DDD si no está instalado
if (!requireNamespace("DDD", quietly = TRUE)) {
install.packages("DDD")
}
# Instalar RevGadgets si no está instalado
if (!requireNamespace("RevGadgets", quietly = TRUE)) {
install.packages("RevGadgets")
}
Instalación de TreePar en R
El paquete TreePar no está disponible directamente desde CRAN, pero podemos instalarlo mediante dos métodos. Si no te funciona el primer método intenta el segundo:
1.- Mediante la liberia remotes
2.- Descargar el paquete TreePar
Puedes descargar el paquete desde el siguiente enlace:
Guarda el archivo en una carpeta de tu elección (por ejemplo, en ~/Descargas/
o C:/Users/TuUsuario/Downloads/
).
Instalar TreePar desde el archivo descargado
Una vez que hayas descargado el archivo TreePar_3.3.tar.gz
, abre R y ejecuta el siguiente código para instalarlo:
# Definir la ruta donde se descargó el archivo
<- "~/Descargas/TreePar_3.3.tar.gz" # Cambia esta ruta según tu sistema operativo
ruta_archivo
# Instalar el paquete desde el archivo .tar.gz
install.packages(ruta_archivo, repos = NULL, type = "source")
Preparación del Árbol Filogenético para Análisis
Para realizar los análisis de tasas de diversificación, primero debemos preparar el árbol filogenético. En esta sección, cargaremos el árbol en R, filtraremos los taxones de interés y generaremos un subárbol optimizado.
Descarga del Árbol Filogenético
📥 Puedes descargar el archivo NEXUS con el árbol de estudio en el siguiente enlace:
📥 Descargar Árbol Filogenético
Guarda el archivo en la carpeta correspondiente y verifica su ubicación antes de continuar.
Cargar y Visualizar el Árbol en R
Ejecuta el siguiente código en R para cargar el archivo y visualizar el árbol completo:
# Cargar los paquetes necesarios
library(ape)
library(ggtree)
library(treeio)
library(tidyverse)
# Definir la ruta del archivo NEXUS
<- "../docs/u1_PatDiv/allSamples.tre" # Asegúrate de cambiar esta ruta si es necesario
archivo_nexus
# Cargar el árbol
<- read.nexus(archivo_nexus)
arbol
# Calcular el Límite Máximo del Eje X
<- max(arbol$edge.length, na.rm = TRUE)
max_edge_length
# Graficar el árbol
ggtree(arbol) +
geom_tiplab(size = 3) +
geom_text2(aes(subset = !isTip, label = node), hjust = -0.3) + # Etiquetar nodos internos
xlim(-0.5, max_edge_length * 1.4) +
theme_tree2()
Extraer los nombres de las terminales de un nodo específico:
Utilizar la función getDescendants
para obtener los nombres de todas la terminales del nodo interno 61:
library(phytools)
<- 61 # Reemplaza este valor por el nodo de tu interés
nodo_interes
# Obtener los índices de los nodos descendientes
<- getDescendants(arbol, nodo_interes)
nodos_internos
# Filtrar solo los índices que corresponden a las terminales (tips)
<- nodos_internos[nodos_internos <= length(arbol$tip.label)]
indices_tips
# Obtener los nombres de las terminales
<- arbol$tip.label[indices_tips]
nombres_tips
# Mostrar los nombres de las terminales
print(nombres_tips)
[1] "06166_50_120_Phod_marmorata" "KRN15_107_Phod_marmorata"
[3] "KRN52_Phod_alticeps" "KRN10_118_Phod_alticeps"
[5] "KRN14_Phod_alticeps" "KRN24_Cyste_armatus"
[7] "KRN02_Cyste_armatus" "KRN25_Cyste_armatus"
[9] "KRN28_Cyste_wislizeni" "KRN03_Cyste_wislizeni"
[11] "KRN26_Cyste_wislizeni" "KRN100_101_Tegro_aloga"
[13] "KRN96_97_98_Tegro_aloga" "KRN11_Tegro_erosa"
[15] "KRN12_Tegro_erosa" "KRN13_Tegro_latecincta"
[17] "KRN53_Tegro_latecincta" "KRN01_Cordy_opaca"
[19] "KRN112_Cordy_opaca" "KRN111_Cordy_fulleri"
[21] "KRN23_Cordy_fulleri" "KRN09_Mege_vittata"
[23] "KRN08_Mege_puncta" "KRN07_133_Mege_cancell"
[25] "KRN129_Mege_cancell_h" "KRN51_Pleu_reticulata"
[27] "KRN48_Pleu_mirabilis" "KRN47_119_Pleu_mirabilis"
[29] "KRN49_Pleu_mirabilis" "KRN19_Eup_sulciphrons"
[31] "KRN131_Eup_schwarzi" "KRN88_89_Eup_histrionica"
[33] "KRN90_Eup_histrionica" "KRN43_108_Eup_fissiceps"
[35] "KRN45_Eup_fissiceps" "KRN44_Eup_fissiceps"
[37] "KRN54_121_Eup_fissiceps" "KRN20_Eup_imperialis"
[39] "KRN06_Eup_imperialis" "KRN42_Eup_imperialis"
[41] "KRN30_113_Eup_elegans" "KRN38_115_Eup_elegans_p"
[43] "KRN29_Eup_elegans_p" "KRN21_114_Eup_elegans_p"
[45] "KRN40_Eup_elegans_p" "KRN92_93_Eup_edmundsi"
[47] "KRN18_135_Eup_viridis" "KRN59_Eup_viridis"
Uso de getMRCA
y extract.clade
para extraer Subárboles Filogenéticos
# Obtener el nodo más reciente en común del ingroup
<- getMRCA(arbol, nombres_tips)
mrca_ingroup
# Extraer el subárbol del ingroup
<- extract.clade(arbol, mrca_ingroup)
subarbol
# Calcular la longitud máxima de las ramas del subárbol
<- max(subarbol$edge.length, na.rm = TRUE)
max_edge_length
# Graficar el subárbol
ggtree(subarbol) +
geom_tiplab(size = 3) +
xlim(-0.5, max_edge_length * 2) +
theme_tree2()
Remover especies duplicadas
# Separar los nombres de las especies (asumiendo el formato "ID_Especie")
<- data.frame(
species_info tip_label = nombres_tips,
species = str_extract(nombres_tips, "[A-Za-z]+_[A-Za-z]+(?:_[A-Za-z]+)?$")
)
# Eliminar las ssp _h y _p
# Seleccionar solo una muestra por especie
<- species_info %>%
unique_species filter(!str_detect(species, "_[hp]$")) %>%
group_by(species) %>%
slice(1) %>% # Selecciona solo la primera aparición de cada especie
ungroup()
# Extraer los nombres de los tips que queremos conservar
<- unique_species$tip_label
selected_tips
# Remover duplicados
<- drop.tip(subarbol, setdiff(subarbol$tip.label, selected_tips))
subarbol_final
ggtree(subarbol_final) +
geom_tiplab(size = 3) +
xlim(-0.5, max(subarbol_final$edge.length) * 2) # Expande el espacio a la izquierda
Guardar el árbol resultante en formato NEXUS
write.nexus(subarbol_final, file="../docs/u1_PatDiv/subarbol_ingroup.nex")
Estimar tasas de especiación y extinción a lo largo del tiempo.
Carga del Árbol Filogenético
# Carga de paquetes
library(subplex)
library(TreeSim)
library(deSolve)
library(ape)
library(TreePar)
# Cargar el árbol desde un archivo Nexus
<- read.nexus("../docs/u1_PatDiv/subarbol_ingroup.nex")
tree
# Visualizar el árbol
ggtree(tree) + theme_tree()
Obtención de los Tiempos de Especiación
Extraeremos y ordenaremos los tiempos de especiación (tiempos de ramificación) del árbol:
# Obtener y ordenar los tiempos de especiación
# La función getx() extrae los tiempos de ramificación del árbol.
<- sort(getx(tree), decreasing = TRUE) # sort () rdena los tiempos en orden descendente.
times <- unname(times) # elimina los nombres de los elementos del vector para simplificar su manipulación.
times print(times)
[1] 19.5829128 18.1869690 16.7465168 15.4799329 15.4702670 15.2568969
[7] 14.8167040 13.6855239 12.2625233 11.9306405 11.3703192 9.3733686
[13] 9.0410043 8.7813081 8.2912126 7.7803338 7.2101697 6.3883760
[19] 6.2512423 1.9309798 0.9252293
Configuración de Parámetros para el Análisis
Definiremos los parámetros necesarios para el análisis de cambios en las tasas de diversificación:
<- 22/26 # Proporción de especies muestreadas (22 de 26 especies)
rho <- 0.2 # Tamaño de la grilla de búsqueda de cambios de tasa (en millones de años)
grid <- min(times) # Tiempo inicial para la búsqueda de cambios de tasa
start <- max(times) # Tiempo final para la búsqueda de cambios de tasa end
Ejecución del Análisis con bd.shifts.optim
Utilizaremos la función bd.shifts.optim
para estimar las tasas de especiación y extinción, así como los puntos en el tiempo donde ocurren cambios significativos en estas tasas:
# Ejecutar el análisis de cambios en las tasas de diversificación
<- bd.shifts.optim(times, rho, grid, start, end, yule=TRUE) result_shifts
[1] "startest"
[1] "test"
# Mostrar los resultados
2]][[1]] result_shifts[[
[1] 70.02138042 0.08912957
A continuación se presentan los valores obtenidos en la estimación de la tasa de diversificación. Dado que el modelo Yule asume que no hay extinción, la tasa de extinción no está definida en este análisis.
Valor de la función de verosimilitud negativa: 70.02138
Tasa de especiación \(\lambda\): 0.08913
En este contexto:
Estos valores indican que la tasa de especiación estimada (\(\lambda\)) es aproximadamente 0.08913.
El valor de la función de verosimilitud negativa (70.02138) proporciona una medida del ajuste del modelo a los datos, donde valores más bajos generalmente indican un mejor ajuste.
Estimación simple de la tasa de diversificación con RevBayes
Crear un script de RevBayes
en Visual Studio Code: divrate.Rev
Cargar el archivo NEXUS
# Cargar la filogenia desde el archivo NEXUS
<- readTrees("../docs/u1_PatDiv/subarbol_ingroup.nex")[1]
T
# Obtener la lista de taxones en la filogenia
<- T.taxa() taxa
Inicializar los vectores de moves y monitors
# Inicializar un vector vacío para los movimientos (moves)
= VectorMoves()
moves
# Inicializar un vector vacío para los monitores (monitors)
= VectorMonitors() monitors
Representación del modelo gráfico para el proceso Yule (Pure-Birth) en RevBayes, donde la tasa de especiación (\(\lambda\)) es tratada como una variable aleatoria extraída de una distribución uniforme.
Especificar la tasa de especiación (\(\lambda\))
# Especificar la tasa de especiación λ con una distribución uniforme
~ dnUniform(0, 100.0) birth_rate
Asignar un movimiento MCMC a la tasa de especiación
# Agregar un movimiento MCMC para la tasa de especiación
moves.append( mvScale(birth_rate, lambda=1.0, tune=true, weight=3.0) )
Especificar la proporción de especies muestreadas (\(\rho\))
# Obtener el número de taxones en la filogenia
<- T.ntips()
num_taxa
# Estimar la proporción de especies muestreadas
<- num_taxa / 26 rho
Obtener la edad de la raíz
# Obtener la edad de la raíz del árbol
<- T.rootAge() root_time
Definir el modelo de tiempo de especiación
El modelo Yule (pure-birth) en RevBayes se define con el proceso de nacimiento y muerte (dnBDP
), pero con la tasa de extinción (mu
) fijada en 0.
# Definir el modelo de diversificación usando un proceso de nacimiento-muerte (BDP)
~ dnBDP(lambda=birth_rate, mu=0.0, rho=rho, rootAge=root_time, samplingStrategy="uniform", condition="survival", taxa=taxa) timetree
Explicación
lambda = birth_rate
→ Tasa de especiación es una variable aleatoria condnUniform(0, 100.0)
.mu = 0.0
→ Asumimos que no hay extinción (modelo Yule).rho = rho
→ Se ajusta según el número de especies muestreadas.rootAge = root_time
→ Condicionamos el modelo en la edad de la raíz.samplingStrategy = "uniform"
→ Asumimos muestreo uniforme.condition = "survival"
→ Solo analizamos árboles que sobrevivieron hasta el presente.
Fijar la filogenia observada
# Fijar la filogenia observada
timetree.clamp(T)
Definir el modelo gráfico
# Crear el objeto de modelo
= model(birth_rate) mymodel
Esto crea un modelo gráfico dirigido acíclico (DAG) donde birth_rate es el nodo principal, y RevBayes automáticamente encuentra todos los otros nodos conectados.
Especificar los Monitores
# Monitor para registrar los estados del modelo en un archivo de salida
monitors.append( mnModel(filename="output/diversification_Yule.log", printgen=10, separator=TAB) )
# Monitor para imprimir la tasa de especiación en la pantalla cada 1000 generaciones
monitors.append( mnScreen(printgen=1000, birth_rate) )
Explicación
mnModel
guarda el registro de la ejecución en"output/diversification_Yule.log"
, escribiendo cada 10 generaciones.mnScreen
imprime la tasa de especiación (birth_rate
) en la consola cada 1000 generaciones.
Configurar y ejecutar el MCMC
# Inicializar el MCMC con dos cadenas combinadas en un solo análisis
= mcmc(mymodel, monitors, moves, nruns=2, combine="mixed")
mymcmc
# Ejecutar el MCMC por 50,000 generaciones, ajustando los movimientos cada 200 generaciones
mymcmc.run(generations=50000, tuningInterval=200)
Explicación
nruns=2
→ Corre dos cadenas independientes.combine="mixed"
→ Combina las cadenas en un solo conjunto de muestras.generations=50000
→ Ejecuta la simulación por 50,000 generaciones.tuningInterval=200
→ Ajusta los movimientos cada 200 generaciones para mejorar la eficiencia del muestreo.
Cargar los resultados en RevGadgets
Después de correr el MCMC en RevBayes, usar RevGadgets en R para analizar la distribución posterior:
# Cargar librerías necesarias
library(RevGadgets)
library(ggplot2)
# Leer los datos del MCMC
<- readTrace("../docs/u1_PatDiv/output/diversification_Yule.log")
mcmc_trace
# Visualizar la distribución posterior de birth_rate
plotTrace(mcmc_trace, vars="birth_rate")
[[1]]
Calcular la media y el intervalo de HPD (Highest Posterior Density)
# Calcular la media posterior y el HPD del 95%
<- summarizeTrace(mcmc_trace, vars="birth_rate")
summary_stats print(summary_stats)
$birth_rate
$birth_rate$trace_1
mean median MAP quantile_2.5 quantile_97.5
0.09313312 0.09214016 0.09139083 0.05919603 0.13341000
Comparar la distribución previa y posterior
library(RevGadgets)
library(ggplot2)
# Leer los datos del MCMC combinando las dos cadenas
<- readTrace(c("../docs/u1_PatDiv/output/diversification_Yule_run_1.log", "../docs/u1_PatDiv/output/diversification_Yule_run_2.log"))
posterior_trace
# Extraer el primer conjunto de muestras (lista de data frames)
<- posterior_trace[[1]]
yule_posterior
# Simular 10,000 valores de la distribución previa
<- data.frame(birth_rate = runif(10000, min=0, max=100))
yule_prior
# Agregar la columna de la distribución previa en el posterior
$birth_rate_prior <- sample(yule_prior$birth_rate, size = nrow(yule_posterior), replace = TRUE)
yule_posterior
# Graficar la comparación
plotTrace(list(yule_posterior), vars = c("birth_rate", "birth_rate_prior"))[[1]] +
theme(legend.position = c(0.80, 0.80), # Ubicación de la leyenda
legend.text = element_text(size =20), # Tamaño del texto de la leyenda
legend.title = element_text(size = 22)) + # Tamaño del título de la leyenda
xlim(0, 1) # Ajusta el límite según los valores observados