En breve
Un forest plot en R sale con tres funciones de metafor: escalc() convierte medias, desviaciones típicas y tamaños muestrales en tamaños del efecto (yi) y sus varianzas (vi); rma(yi, vi) ajusta el modelo de efectos aleatorios por REML; y forest(res, header = TRUE) dibuja el gráfico. A partir de ahí personalizas las etiquetas, metes la I² y la Q bajo el rombo con mlab, añades rombos de subgrupo con addpoly() y exportas a 300 dpi con png(..., res = 300) o en PDF vectorial.
Saber leer e interpretar un forest plot es una cosa; construir el tuyo y que salga con calidad de publicación es otra. En este tutorial vas a hacer un forest plot completo en R con el paquete metafor (la referencia para metaanálisis en R) desde unos datos brutos de medias y desviaciones típicas hasta un gráfico listo para enviar a una revista Q1. Todo el código es reproducible: cópialo, cámbialo por tus datos y funciona.
Si al final solo quieres el gráfico y no necesitas dominar el código, el generador de forest plot hace este mismo cálculo (efectos fijos y aleatorios, heterogeneidad) desde el navegador y te descarga el PNG en alta resolución.
Qué necesitas antes de empezar
Un forest plot resume un metaanálisis, así que necesitas datos de varios estudios sobre la misma pregunta. Hay dos escenarios habituales:
- Tienes los datos brutos: para cada estudio, las medias, desviaciones típicas y tamaños muestrales de los dos grupos (tratamiento y control). A partir de ahí calcularás el tamaño del efecto.
- Tienes los efectos ya calculados: para cada estudio, el tamaño del efecto (por ejemplo, una d de Cohen o un odds ratio, que puedes obtener con nuestra calculadora de odds ratio si partes de una tabla 2×2) y su varianza o error estándar. Esto suele venir de tablas de artículos previos.
En ambos casos, el flujo en metafor es el mismo: (1) obtener el efecto y su varianza con escalc(), (2) ajustar el modelo con rma() y (3) dibujar con forest(). Vamos paso a paso.
Paso 1: Instalar y cargar metafor
# Solo la primera vez
install.packages("metafor")
library(metafor)
El paquete metafor (Viechtbauer, 2010) es, con diferencia, el más usado para metaanálisis en R y el que esperan ver los revisores. Está activamente mantenido y cubre desde el metaanálisis básico hasta metarregresión, modelos multinivel y modelos de efectos robustos.
Paso 2: Preparar los datos y calcular el tamaño del efecto
Supongamos que tienes seis estudios que comparan una intervención psicológica frente a un grupo control sobre una medida de ansiedad. Montamos el data frame y calculamos el tamaño del efecto con escalc():
misdatos <- data.frame(
estudio = c("García 2019", "López 2020", "Martínez 2021",
"Chen 2021", "Fernández 2022", "Sánchez 2023"),
m_trat = c(22.1, 19.8, 24.5, 21.0, 26.2, 20.4),
sd_trat = c(5.2, 4.8, 6.1, 5.5, 5.9, 4.6),
n_trat = c(40, 65, 32, 58, 45, 70),
m_ctrl = c(24.8, 21.5, 27.0, 22.3, 29.1, 22.0),
sd_ctrl = c(5.4, 5.0, 6.3, 5.2, 6.0, 4.9),
n_ctrl = c(38, 63, 30, 55, 44, 68)
)
# Diferencia de medias estandarizada (g de Hedges, corregida por muestra pequeña)
dat <- escalc(measure = "SMD",
m1i = m_trat, sd1i = sd_trat, n1i = n_trat,
m2i = m_ctrl, sd2i = sd_ctrl, n2i = n_ctrl,
data = misdatos, slab = estudio)
Tras esto, dat tiene dos columnas nuevas: yi (el tamaño del efecto, en este caso la g de Hedges) y vi (su varianza muestral). El argumento measure = "SMD" devuelve la g de Hedges, que aplica la corrección por sesgo de muestra pequeña sobre la d de Cohen, el estándar en psicología. Si trabajas con datos dicotómicos, usa measure = "OR" (odds ratio) o "RR" (riesgo relativo) con los argumentos de frecuencias (ai, bi, ci, di); y si ya tienes los efectos calculados, salta este paso y pasa directamente tus columnas yi y vi al modelo.
El argumento slab (de study label) define las etiquetas de cada estudio que aparecerán a la izquierda del gráfico. Defínelo aquí y no tendrás que repetirlo después.
Paso 3: Ajustar el modelo de efectos aleatorios
res <- rma(yi, vi, data = dat, method = "REML")
res
La función rma() ajusta un modelo de efectos aleatorios por defecto (estimador REML), que es el estándar en psicología y ciencias de la salud: asume que cada estudio estima un efecto verdadero distinto extraído de una distribución, e incorpora la varianza entre estudios (τ²) al cálculo. Si por algún motivo justificado necesitas efectos fijos, usa method = "FE", pero salvo excepciones muy concretas, mantén REML.
Al imprimir res obtienes el efecto combinado, su intervalo de confianza, y los indicadores de heterogeneidad: I², τ² y el test Q. Conviene que entiendas qué significan antes de dibujar nada; si el I² es alto, el rombo del forest plot será un promedio poco representativo. La guía sobre heterogeneidad en metaanálisis (I²) explica cómo interpretarlos y cuándo el efecto combinado deja de ser informativo.
Paso 4: Dibujar el forest plot básico
Con el modelo ajustado, una sola línea genera el gráfico:
forest(res)
Esto ya produce un forest plot funcional: una fila por estudio con su cuadrado proporcional al peso y su intervalo de confianza, el rombo del efecto combinado al pie y el eje con la escala del efecto. Pero para publicación conviene afinarlo.
forest(res) con etiquetas y columna de efectos: seis estudios y el rombo del modelo de efectos aleatorios (RE Model).Paso 5: Personalizar para calidad de publicación
Estos argumentos cubren el 95% de lo que necesitas para que el gráfico quede como en un artículo Q1:
forest(res,
header = c("Estudio", "g [IC 95%]"), # cabeceras de columnas
xlab = "g de Hedges", # etiqueta del eje
mlab = "Modelo de efectos aleatorios (REML)", # etiqueta del rombo
slab = dat$estudio, # etiquetas de estudio
order = "obs", # ordenar por tamaño del efecto
cex = 0.9) # tamaño de fuente
Algunos argumentos útiles adicionales: xlim y alim controlan los límites del área de dibujo y del eje; at fija las marcas del eje (por ejemplo at = c(-1, -0.5, 0, 0.5)); refline mueve la línea de nulidad (por defecto 0 para diferencias, conviene refline = 1 si trabajas con odds ratios); y ilab permite añadir columnas extra como los tamaños muestrales. Para odds ratios o riesgos relativos, añade atransf = exp para que el eje muestre la escala original en lugar de la logarítmica.
Paso 6: Añadir los estadísticos de heterogeneidad al gráfico
Un forest plot publicable muestra I², Q y τ², normalmente bajo el rombo. Puedes incluirlos automáticamente con bquote y mlab:
forest(res, header = TRUE, xlab = "g de Hedges",
mlab = bquote(paste("RE Model (Q = ",
.(formatC(res$QE, digits = 2, format = "f")),
", df = ", .(res$k - res$p),
", p = ", .(formatC(res$QEp, digits = 3, format = "f")),
"; ", I^2, " = ", .(formatC(res$I2, digits = 1, format = "f")), "%)")))
Así el lector ve de un vistazo si la heterogeneidad invalida la lectura del rombo, sin tener que buscarla en el texto.
Forest plot con subgrupos
Si una variable moderadora (por ejemplo, el formato de la intervención: presencial vs. online) puede explicar la heterogeneidad, conviene un forest plot con subgrupos. La forma más limpia es ajustar el modelo con el moderador y dibujar los estudios agrupados, añadiendo un rombo parcial por subgrupo con addpoly():
# Modelo con moderador categórico
res.mod <- rma(yi, vi, mods = ~ formato, data = dat)
# Submodelos por nivel
res.pres <- rma(yi, vi, data = dat, subset = (formato == "presencial"))
res.onl <- rma(yi, vi, data = dat, subset = (formato == "online"))
# Dibujar ordenando por subgrupo y añadir rombos parciales
forest(res, order = order(dat$formato), addfit = FALSE,
header = TRUE, xlab = "g de Hedges")
addpoly(res.pres, row = 1, mlab = "Subtotal: presencial")
addpoly(res.onl, row = 5, mlab = "Subtotal: online")
Ajusta los valores de row al número de estudios de cada subgrupo. El test de diferencias entre subgrupos (Qbetween) lo obtienes del res.mod: si es significativo, el moderador explica parte de la variación entre estudios.
Exportar el gráfico a calidad de publicación
Las revistas piden imágenes a 300 dpi o más, y muchas exigen formato vectorial (PDF, EPS, TIFF). Abre un dispositivo gráfico, dibuja y ciérralo:
# PNG a 300 dpi
png("forest_plot.png", width = 8, height = 6, units = "in", res = 300)
forest(res, header = TRUE, xlab = "g de Hedges")
dev.off()
# PDF vectorial (recomendado por muchas revistas)
pdf("forest_plot.pdf", width = 8, height = 6)
forest(res, header = TRUE, xlab = "g de Hedges")
dev.off()
El formato vectorial (PDF/EPS) no pierde calidad al ampliar y suele ser la opción preferida del editor de producción. Ajusta width y height hasta que las etiquetas no se solapen.
Cuatro errores frecuentes al crear el forest plot
1. Pasar la desviación típica en lugar de la varianza a vi. El segundo argumento de rma() es la varianza muestral del efecto, no el error estándar ni la desviación típica. Si pasas el error estándar, usa sei en su lugar (rma(yi, sei = mi_se, ...)). Confundirlos infla o reduce artificialmente la precisión y deforma los pesos.
2. Saltarse escalc() y meter medias brutas. Un forest plot necesita efectos en una escala común. Si comparas estudios con medias en escalas distintas sin estandarizar, el gráfico no tiene sentido. escalc() hace esa conversión y, además, corrige el sesgo de muestra pequeña en la g de Hedges.
3. Olvidar el signo del efecto. Decide desde el principio qué dirección es «mejora» y comprueba que todos los estudios estén codificados igual. Un estudio con la resta invertida (control − tratamiento en vez de tratamiento − control) aparecerá en el lado equivocado y sesgará el efecto combinado.
4. Usar efectos fijos sin justificarlo. Aunque rma() usa efectos aleatorios por defecto, a veces se copia código ajeno con method = "FE". Salvo que tengas un argumento sólido (muy pocos estudios funcionalmente idénticos), mantén efectos aleatorios: es lo que esperan los revisores en psicología y salud.
Alternativa: el paquete meta
Si prefieres una sintaxis algo más directa, el paquete meta también genera forest plots publicables. metacont() para datos continuos o metabin() para datos binarios calculan el efecto y el modelo en una sola función, y forest() dibuja el gráfico con un estilo ligeramente distinto. Tanto metafor como meta son opciones válidas y aceptadas en revistas indexadas; metafor ofrece más control y flexibilidad para análisis avanzados (multinivel, metarregresión, modelos robustos), por lo que es la que recomiendo si vas a crecer en complejidad.
Y si lo que necesitas es un forest plot puntual sin instalar R ni escribir código, el generador de forest plot y funnel plot hace el mismo cálculo (efectos fijos y aleatorios, heterogeneidad) desde el navegador y descarga el PNG en alta resolución, sin marca de agua.
Y cuando el gráfico esté listo
Crear el forest plot es el penúltimo paso. Antes de cerrar el análisis, comprueba el sesgo de publicación con un funnel plot y el test de Egger, y asegúrate de saber interpretar cada elemento del forest plot antes de redactar la discusión. Para la redacción final, la guía de cómo reportar resultados en APA 7 cubre el formato que esperan las revistas.
Antes de enviarlo: un error estándar colado donde rma() esperaba una varianza, o un modelo de efectos fijos que nadie ha justificado, sobreviven al gráfico y llegan intactos al revisor, junto con cosas más serias que ya no ves porque llevas meses dentro del manuscrito. Si el metaanálisis ya está redactado, pásalo por el revisor de artículos: una pre-revisión gratuita estilo Reviewer 2 que te dice qué le van a objetar antes de que la objeción llegue en forma de desk-rejection.
Preguntas frecuentes
¿Qué funciones de metafor necesito para hacer un forest plot?
Tres. escalc() calcula el tamaño del efecto y su varianza a partir de los datos brutos de cada estudio, rma() ajusta el modelo metaanalítico y forest() dibuja el gráfico. Si ya tienes los efectos y sus varianzas calculados en una hoja de cálculo, te saltas escalc() y pasas directamente tus columnas yi y vi a rma().
¿Cómo preparo los datos si solo tengo medias y desviaciones típicas?
Monta un data frame con una fila por estudio y seis columnas: media, desviación típica y n del grupo de tratamiento, y lo mismo del grupo control. Luego llama a escalc(measure = "SMD", ...), que devuelve la g de Hedges (la d de Cohen corregida por sesgo de muestra pequeña) en yi y su varianza en vi. Para datos dicotómicos usa measure = "OR" o "RR" con las frecuencias.
¿Qué modelo ajusta rma() por defecto?
Un modelo de efectos aleatorios con estimador REML, que es justo lo que se espera en psicología y ciencias de la salud. Solo se cambia a efectos fijos (method = "FE") con una justificación sólida, y ese cambio hay que explicarlo en el manuscrito: al ignorar la varianza entre estudios, el intervalo del efecto combinado sale artificialmente estrecho.
¿Cómo añado la I² y la Q al forest plot?
Con el argumento mlab de forest(), componiendo la etiqueta del rombo con bquote() a partir de los valores que devuelve el propio modelo (res$QE, res$QEp, res$I2). Así el lector ve la Q, sus grados de libertad, su p y la I² justo debajo del rombo, sin tener que buscarlos en el texto.
¿Cómo hago un forest plot con subgrupos en R?
Ajusta el modelo con el moderador (mods = ~ formato) para obtener el contraste entre subgrupos, ajusta un submodelo por nivel con subset, dibuja los estudios ordenados por subgrupo con order y desactiva el rombo automático con addfit = FALSE. Después añades un rombo parcial por subgrupo con addpoly(), ajustando el argumento row al número de estudios de cada uno.
¿Cómo exporto el forest plot con la calidad que piden las revistas?
Abre un dispositivo gráfico, dibuja y ciérralo: png("forest.png", width = 8, height = 6, units = "in", res = 300), la llamada a forest() y dev.off(). Para formato vectorial, que es lo que suele preferir el editor de producción, cambia png() por pdf(). Ajusta el ancho y el alto hasta que las etiquetas de los estudios no se solapen.
¿Quieres un metaanálisis completo, con forest plots a prueba de revisores?
Soy doctor en psicología y ejecuto metaanálisis de principio a fin en R: búsqueda sistemática, codificación, modelo de efectos aleatorios, forest plots, heterogeneidad y sesgo de publicación, con la redacción APA 7 lista para enviar.
Pídeme un diagnóstico gratis →