Running a meta-analysis in R means using free, open-source packages, above all meta and metafor, to pool effect sizes from multiple studies, quantify heterogeneity, draw forest and funnel plots, and probe the result with subgroup analysis, meta-regression, and publication bias tests. R gives you every modern estimator and model the methods literature offers, and because the whole analysis lives in a script, it is fully reproducible: anyone can rerun your synthesis from data to plot with one command.
meta versus metafor: two packages, two philosophies
Almost every tutorial funnels you toward one of two packages, and the difference is philosophy rather than capability. meta (by Guido Schwarzer) is the high-level option: one function call takes your raw study data, computes effect sizes, pools them, and prints a publication-style summary, with sensible defaults throughout. metafor (by Wolfgang Viechtbauer) is the modelling toolkit: you compute effect sizes explicitly, then fit exactly the model you specify, which makes it the package of choice for multilevel models, multivariate outcomes, and anything unusual. The published comparison of the two reaches the sensible conclusion: meta is quicker to learn and covers standard reviews; metafor has the steeper curve and the higher ceiling. Many analysts use both, meta for the main synthesis and metafor when the model outgrows it. Installation is a one-off: install.packages(c("meta", "metafor")) pulls both from CRAN, and neither costs anything, which already separates them from every commercial competitor. Where these sit against RevMan, Stata, and the commercial tools is mapped in our comparison of meta-analysis software.
Getting your data into R
Every R meta-analysis starts with a flat table: one row per study, one column per statistic. For binary outcomes that means events and totals in each arm; for continuous outcomes, means, standard deviations, and sample sizes; for precomputed effects, the estimate and its standard error, with ratio measures on the log scale. Read the file in with read.csv() or the readxl package, and resist the temptation to tidy inside Excel: every correction made in the spreadsheet by hand is invisible to the record, while a correction made in the script is documented forever. Two gotchas recur. Standard errors are not standard deviations, and confusing them silently wrecks the weights; and studies reporting medians, interquartile ranges, or confidence intervals instead of means and standard deviations need converting first, using published formulas, before the pooling functions see them. Ten minutes checking the data frame with str() and a scatter of effect against sample size catches most extraction slips before they become retractions. Keep the raw extraction sheet untouched and do every transformation in code, so the chain from published paper to pooled estimate remains inspectable at each link.
A worked flow with metagen and metabin
In the meta package, the function matches the data type. With a spreadsheet of binary outcomes, events and totals per arm, metabin(event.e, n.e, event.c, n.c, sm = "RR") computes and pools risk ratios using Mantel-Haenszel weighting by default. Continuous outcomes go to metacont() for mean differences or standardised mean differences, while metagen(TE, seTE) is the generic route: it accepts any precomputed effect and standard error, which is how you pool hazard ratios or adjusted estimates. Log-scale measures are handled internally, and summary() on the result prints the pooled estimate, confidence and prediction intervals, and heterogeneity statistics in one block. The result is an ordinary R object, so the pooled estimate, its interval, and every study-level quantity can be extracted by name and piped into tables or further analyses, rather than retyped from printed output. Choosing which effect measure to feed these functions is its own methodological question, covered in our guide to effect sizes in meta-analysis.
escalc and rma in metafor
The metafor workflow splits the same job into two explicit steps. escalc(measure = "RR", ai, n1i, ci, n2i) turns raw data into effect sizes (yi) and sampling variances (vi) for dozens of measures, from log risk ratios to raw correlations. Then rma(yi, vi, method = "REML") fits the random-effects model, and variations on the same call fit fixed-effect models, meta-regression (add mods = ~ dose), and multilevel structures with rma.mv() when effect sizes are nested within studies. The fitted object answers follow-up questions directly: predict() returns the pooled effect back-transformed to the natural scale with its prediction interval, and influence() reports leave-one-out diagnostics for every study. The two-step design feels slower at first but pays off the moment your review has dependent effect sizes or needs a model the one-line functions cannot express. Dependence is the quiet killer in many reviews: when one study contributes several effect sizes, from multiple scales, timepoints, or treatment arms, treating them as independent overstates precision, and metafor’s multilevel and robust variance options are the standard remedy.
Choosing the model and the tau-squared estimator
R will not choose your model for you. The fixed-effect versus random-effects decision should be made at protocol stage, and in most reviews the random-effects model is the defensible default. Where R beats point-and-click tools is the tau-squared estimator: instead of being locked to the dated DerSimonian and Laird method, you can, and usually should, use REML (restricted maximum likelihood), the estimator methodologists now recommend, with the Hartung-Knapp adjustment (method.random.ci = "HK" in meta, test = "knha" in metafor) to widen confidence intervals honestly when studies are few. These two settings, REML plus Hartung-Knapp, are close to the current methodological consensus for a standard random-effects synthesis, and being able to state them explicitly in the methods section is itself a mark of a careful review. Both packages report I-squared, tau-squared, and the Q test alongside the pooled effect; what those statistics actually mean is the subject of our guide to interpreting heterogeneity.
Forest and funnel plots
Both packages draw excellent graphics. forest() on a meta or metafor object produces a forest plot with study labels, weights, the pooled diamond, and heterogeneity statistics, and nearly every visual element can be customised, columns added, studies ordered by effect, subgroup panels stacked. funnel() draws the corresponding funnel plot, with contour-enhanced versions that shade significance regions so you can judge whether asymmetry tracks statistical significance. Reading these figures is a skill of its own; see our walkthroughs of forest plot interpretation and funnel plot interpretation, and if you only need a quick figure without writing code, our free forest plot generator builds one in the browser. For manuscripts, save plots programmatically with pdf() or png() at the resolution the journal specifies, so the figure regenerates identically whenever the data change; a plot exported by hand from a graphics window is a plot someone will eventually fail to reproduce.
Subgroup analysis and meta-regression
Moderator questions are where R pulls decisively ahead of RevMan. In meta, passing subgroup = design to any pooling function splits the analysis by a categorical variable and tests for subgroup differences. In metafor, rma(yi, vi, mods = ~ year + dose) fits a full meta-regression with continuous or mixed moderators, an omnibus moderator test, and residual heterogeneity estimates, something RevMan simply cannot do. The statistical traps, low power, confounded moderators, ecological bias, are real, and our guide to subgroup analysis and meta-regression covers when these analyses inform and when they mislead. Two practical habits keep the exploration honest: pre-specify the moderators in the protocol so the analysis tests hypotheses rather than trawling for them, and report the number of studies per subgroup, because a moderator test built on three studies a side is noise dressed as insight whatever the p value says.
Publication bias tests
R implements the full small-study toolkit: metabias() runs the Egger regression test and rank-correlation alternatives, trimfill() applies the trim-and-fill adjustment, and metafor adds selection models and PET-PEESE style regressions for more principled sensitivity analysis. None of these tests proves or excludes publication bias; they detect small-study effects that have several possible causes, a distinction our article on publication bias in meta-analysis explains. Run them only with a reasonable number of studies, commonly ten or more, and report them as sensitivity analyses rather than verdicts. When the trim-and-fill adjusted estimate and the selection model tell the same story as the primary analysis, say so, because concordance across methods is far more persuasive to reviewers than any single test; when they disagree, the disagreement itself is the finding, and the discussion section should own it rather than bury it.
The dmetar companion
The free online book Doing Meta-Analysis with R by Harrer and colleagues comes with a companion package, dmetar, that fills practical gaps: find.outliers() flags influential studies, InfluenceAnalysis() automates leave-one-out diagnostics, and power.analysis() estimates the power of a planned synthesis. For most learners the book plus meta plus dmetar is the fastest credible route into R-based synthesis, and the influence diagnostics slot naturally into the sensitivity analyses a good review reports. The book is free to read online, walks through every function with worked health examples, and is honest about the judgement calls the code cannot make for you, which is precisely the combination a first-time analyst needs.
Reporting what you ran
A script only earns its reproducibility credentials if the manuscript describes it faithfully. The methods section should name the packages with version numbers, the effect measure, the model, the tau-squared estimator, whether the Hartung-Knapp adjustment was applied, and the pre-specified subgroup and sensitivity analyses, because two random-effects analyses of the same data can legitimately disagree when those settings differ. Report the prediction interval alongside the confidence interval for random-effects models, state the threshold logic used for the bias tests, and archive the script and the extraction sheet in a repository so the words “available on request” never appear. Journals increasingly ask for exactly this bundle, and supplying it costs minutes when the analysis was scripted from the start.
Why scripts win, and when R is overkill
The deepest argument for R is not any single method but reproducibility. A script is a complete, rerunnable record of every decision: change one extracted number and every estimate, plot, and table regenerates identically; a journal reviewer asks for a different estimator and the revision is one line. Point-and-click tools cannot offer that audit trail. The honest counterpoint is that R is overkill for some projects. A standard pairwise review with straightforward outcomes, no meta-regression, and a team that already knows RevMan will finish faster there, and a quick pooled estimate for a feasibility check needs nothing more than our free meta-analysis calculator. Learn R when your questions outgrow those tools, and learn it from a worked example in your own field rather than abstract documentation, because the fastest route to fluency is reproducing a published analysis end to end. The full sequence of an analysis, whatever the software, is set out in our guide to conducting a meta-analysis.