Meta-Analysis Workshop Software Workflow Version 1.0
Author
Alex Shaw
About this workflow
This is the software workflow for our Introduction to Meta-Analysis workshop. The slides for the presentation accompanying this workflow can be found here.
In this workflow we focus on practical data analysis by presenting the steps to run a meta-analysis using the metafor package in R. The R code used to create output is also included.
Acknowledgements
If you use this workflow, please don’t forget to acknowledge us. You can use the following sentence
The authors acknowledge the Statistical Workshops and Workflows provided by the Sydney Informatics Hub, a Core Research Facility of the University of Sydney
The data comprises 34 studies assessing the effectiveness of cholesterol treatments on mortality in randomised controlled trials.
Step 1: R setup
Before we delve into the content, it is necessary to load certain libraries. If you want to use them, please install them beforehand with install.packages("library_name").
Code
# Run once# install.packages("metafor")library(metafor)
Loading required package: Matrix
Loading required package: metadat
Loading required package: numDeriv
Loading the 'metafor' package (version 4.8-0). For an
introduction to the package please type: help(metafor)
Load the data. Note: check file is in your working directory.
We use the escalc function from metafor to create yi which is the log OR effect size and vi which is the variance of yi.
When we view the model object meta.es, the effect size and variance sit alongside our inputs.
Step 3: create a meta-analysis summary using rma()
Code
meta.re <-rma(yi, vi, data = meta.es) #REML estimation by default# Use the method argument to change estimation to Der Simonian and Laird, etc.# for example: meta.reDL <-rma(yi, vi, data = meta.es, method = "DL")# Now convert summary estimate log(OR) to ORmeta.re.or <-exp(c(meta.re$b, meta.re$ci.lb, meta.re$ci.ub))summary(meta.re)
Random-Effects Model (k = 34; tau^2 estimator: REML)
logLik deviance AIC BIC AICc
-22.9313 45.8625 49.8625 52.8556 50.2625
tau^2 (estimated amount of total heterogeneity): 0.0478 (SE = 0.0269)
tau (square root of estimated tau^2 value): 0.2186
I^2 (total heterogeneity / total variability): 53.87%
H^2 (total variability / sampling variability): 2.17
Test for Heterogeneity:
Q(df = 33) = 89.1065, p-val < .0001
Model Results:
estimate se zval pval ci.lb ci.ub
-0.1178 0.0628 -1.8750 0.0608 -0.2410 0.0053 .
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Code
# Print OR resultscat("Odds Ratio (OR):", round(meta.re.or[1], 3), "\n","95% CI: [", round(meta.re.or[2], 3), ",", round(meta.re.or[3], 3), "]\n")
Odds Ratio (OR): 0.889
95% CI: [ 0.786 , 1.005 ]
Code
# For a fixed effects model, use method="FE"meta.fe <-rma(yi, vi, data = meta.es, method ="FE")summary(meta.fe)
# Now convert summary estimate log(OR) to ORmeta.fe.or <-exp(c(meta.fe$b, meta.fe$ci.lb, meta.fe$ci.ub))cat("Odds Ratio (OR):", round(meta.fe.or[1], 3), "95% CI: [", round(meta.fe.or[2], 3), ",", round(meta.fe.or[3], 3), "]\n")
Odds Ratio (OR): 0.844 95% CI: [ 0.793 , 0.9 ]
Random-Effects model has
Reduced OR Effect size from 0.84 to 0.89 (closer to 1)
Increased width of CI from 0.79~0.90 to 0.79~1.01
CI for effect size now traverses 1.00 (just)
Create Forest Plots
Code
# Create Forest Plots# First plot is for Random-Effects modelforest( meta.re,header =TRUE,atransf = exp, #display OR on x-axis log scaleat =log(c(0.01, 0.1, 1, 10,50)),xlim =c(-8, 8),xlab ="Random Effects Model",mlab ="Random Effects OR summary estimate",showweights =TRUE,pch =15,border ="red", #colour of summary diamond bordercol ="red", #colour of summary diamond fillmain ="The effect of cholesterol lowering interventions \n on mortality \n Sutton-Smith data")op <-par(cex =0.75, font =2)text(c(-2, 2), 36, c("Favours Treatment", "Favours Control"))
Code
par(op)# Second plot for Fixed-Effect modelforest( meta.fe,header =TRUE,atransf = exp, #display OR on x-axis log scaleat =log(c(0.01, 0.1, 1, 10, 50)),xlim =c(-8, 8),xlab ="Fixed-Effect Model",mlab ="Fixed-Effect OR summary estimate",showweights =TRUE,pch =15,border ="red", #colour of summary diamond bordercol ="red", #colour of summary diamond fillmain ="The effect of cholesterol lowering interventions \n on mortality \n Sutton-Smith data")op <-par(cex =0.75, font =2)text(c(-2, 2), 36, c("Favours Treatment", "Favours Control"))
Code
par(op)
Note estimates and CI’s for each study. Each study’s weight designated by size of square.
Graphical representation of (moderate) heterogeneity.
Weightings are different between the two models
Step 4: Explore Heterogeneity
Refer to the output from the rma() function for both models.
No additional code required.
Q statistic is significant p<0.0001 (heterogeneous studies)
I squared (proportion of inconsistency) 54%~63% is moderate
Tau squared estimate is > zero
We expect studies to have underlying differences in effect size
We should use random effects
Step 5: Publication Bias
Code
# Basic Funnel Plotfunnel(meta.re, main ="Random Effects Model")
Code
# We can use trimfill(), ranktest(), regtest(), hc() as tests for bias# Use the trimfill() function to see where "missing" studies might be
Check for asymmetry * No of studies > 10 * Studies are of various sizes and precision * Plot appears to be symmetrical (can use random effects model without problem) * One more check
Code
meta.tf <-trimfill(meta.re)funnel(meta.tf, main ="Trim and Fill funnel plot \n for RE model")
Note: 3 imputed studies (open circles)
Code
# Other diagnostic plots like Radial (Galbraith) plot are available# aslo labbe(), qqnorm(), baujat(), gosh()# lets look at the Radial plotsradial(meta.re, main ="Random Effects model")
Code
radial(meta.fe, main ="Fixed Effects model")
Code
# lets look at some Q-Q plotsqqnorm(meta.re, main ="Random Effects Model")
Code
qqnorm(meta.fe, main ="Fixed Effects Model")
Code
# Can also produce influence statisticsinfl <-influence(meta.re)plot(infl, plotdfb =TRUE)
Step 6: Sub-group analysis (not included for this example)
The original study looked at a sub-group analysis using the factor: Coronary heart disease risk group – high, medium, low
Sub-group analysis found significant differences between these groups, with only the high risk CHD (treatment) group showing a benefit in terms of total mortality.
A meta-regression was also carried out showing a similar trend.