A 'ggplot2' extension that creates publication-quality forest and funnel plots from 'meta' package objects or tidy data frames. Provides custom 'ggproto' geometries for study-level confidence intervals with weight-proportional squares, summary effect diamonds, prediction intervals, null-effect reference lines, and funnel-plot contours. Supports subgroup analysis, back-transformation of summary measures, on-the-fly effect pooling, effect and weight table columns, and journal-specific layout presets (JAMA, BMJ, RevMan5).

ggmeta builds publication-quality forest and funnel plots with
ggplot2. Give it a meta object (from
the meta package) or a plain
tidy data frame — the result is an ordinary ggplot you can theme,
compose, and save.
Install the released version from CRAN:
install.packages("ggmeta")
Or the development version from GitHub:
# install.packages("remotes")
remotes::install_github("drhrf/ggmeta")
No meta package required — a tidy data frame of effect sizes and
standard errors is enough. Set add_summary = TRUE to pool the studies
on the fly (inverse-variance common effect and DerSimonian–Laird random
effects):
library(ggmeta)
studies <- data.frame(
studlab = c("Trial 1", "Trial 2", "Trial 3", "Trial 4", "Trial 5"),
estimate = c(0.10, 0.35, 0.22, 0.48, 0.05),
se = c(0.12, 0.10, 0.14, 0.16, 0.11)
)
studies$ci_lower <- studies$estimate - 1.96 * studies$se
studies$ci_upper <- studies$estimate + 1.96 * studies$se
ggforest(studies, add_summary = TRUE)
Pass a meta object and add columns = TRUE to reproduce the familiar
meta::forest() table: an effect estimate, 95% CI, and weight column
for every study and summary, headers, and a heterogeneity line (I²,
the between-study variance, Q, and p) — all as a plain ggplot.
library(meta)
#> Loading required package: metabook
#> Loading 'meta' package (version 8.5-0).
#> Type 'help(meta)' for a brief overview.
dat <- data.frame(
study = c("Adams 2019", "Baker 2020", "Chen 2020",
"Diaz 2021", "Evans 2022", "Foster 2023"),
event.e = c(12, 8, 25, 18, 30, 15), n.e = c(120, 90, 200, 150, 250, 130),
event.c = c(20, 14, 30, 28, 35, 25), n.c = c(118, 92, 205, 148, 245, 128)
)
m <- metabin(event.e, n.e, event.c, n.c,
data = dat, studlab = study, sm = "RR")
ggforest(m, columns = TRUE)
Everything is optional. Choose which columns to show, and toggle the other elements on or off:
ggforest(m, columns = c("estimate", "ci")) # only some columns
ggforest(m, effect_header = "Risk ratio") # rename the estimate column
ggforest(m, show_hetstats = FALSE) # hide the heterogeneity line
ggforest(m, show_predict = FALSE) # hide the prediction interval
ggforest(m, sort_studies = FALSE) # keep the input order
ggmeta also draws funnel plots with ggfunnel() (study effect
vs. standard error, with pseudo confidence-interval contours). And
because every plot is an ordinary ggplot, a forest and a funnel
compose on a single figure with
patchwork — something that is
awkward with the base-graphics output of meta:
library(patchwork)
(ggforest(m) | ggfunnel(m)) +
plot_layout(widths = c(2, 1)) +
plot_annotation(tag_levels = "A")
Layout presets restyle a plot for common journals. They are ordinary
ggplot2 components, so you add them with +:
layout_jama(ggforest(m, columns = TRUE))
layout_bmj() and layout_revman5() are also available.
For a column of your own — sample sizes, events, anything — use
geom_forest_text(), aligned to the study rows through the shared y.
tidy_meta() exposes the same tidy data frame ggforest() builds
internally, and format_effect() builds "estimate (low to high)"
labels:
td <- tidy_meta(m)
studies <- td[!td$is_summary, ]
ggforest(m) +
geom_forest_text(aes(y = studlab, label = n.e), data = studies,
x = 4, hjust = 0) +
expand_limits(x = 6)
ggsave().ggforest() and ggfunnel(), both
ordinary ggplots, so you can arrange them together with patchwork.meta::forest()-style tables — estimate / CI / weight columns and
a heterogeneity caption, via columns = TRUE.meta — works on a tidy data frame or a meta
object, and can pool studies itself (add_summary = TRUE).ggproto geoms for CIs, summary
diamonds, prediction intervals, reference lines, and text columns.layout_jama(), layout_bmj(),
layout_revman5().vignette("getting-started") — a tour of the package.vignette("customising") — restyle every element of a forest or
funnel plot.vignette("from-meta-forest") — coming from meta::forest().