0% found this document useful (0 votes)
243 views9 pages

Metafor Tutorial

The document provides tutorials for conducting meta-analyses in R, including tutorials on fixed and random effects models, confidence and prediction intervals, moderators analysis, funnel plots, forest plots, and customizing forest plots. It demonstrates how to import data from Excel, perform meta-analyses using the metafor package, and generate forest plots, funnel plots, and other outputs. Key steps include importing data, specifying fixed or random effects models, adding moderators, and customizing labels, scales, and other aspects of forest and funnel plots.

Uploaded by

Surya Dila
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
243 views9 pages

Metafor Tutorial

The document provides tutorials for conducting meta-analyses in R, including tutorials on fixed and random effects models, confidence and prediction intervals, moderators analysis, funnel plots, forest plots, and customizing forest plots. It demonstrates how to import data from Excel, perform meta-analyses using the metafor package, and generate forest plots, funnel plots, and other outputs. Key steps include importing data, specifying fixed or random effects models, adding moderators, and customizing labels, scales, and other aspects of forest and funnel plots.

Uploaded by

Surya Dila
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 9

####Tutorial 8: Fixed and Random Effects for Odd Ratio/Risk Ratio with Preferred

Binary Input####
# Loading library (if not yet)
library(metafor)
library(xlsx)

# Read excel data, print it


MindfulDat<- read.xlsx("C:\\Users\\Surya Dila\\Desktop\\R tutorial M
Brannick\\8\\Mindful.xlsx", sheetName = "Sheet1")
MindfulDat

# Meta analysis of binary data (preferred format), print it


MindfulRes1 <- rma(ai=A, bi=B, ci=C, di=D, measure="OR", method="DL",
data=MindfulDat)
MindfulRes1
MindfulRes2 <- rma(ai=A, bi=B, ci=C, di=D, measure="RR", method="DL",
data=MindfulDat)
MindfulRes2

# Meta analysis when one or more zero value in 2x2 cells (Three choices: leave it
alone (reported as missing), add small value to all cells (ex: add 0.5 to all
cells), or add small value only to zero value cell)
MindfulRes3 <- rma(ai=A, bi=B, ci=C, di=D, measure="OR", method="DL", add=.5, to=
"all", data=MindfulDat)
MindfulRes3
MindfulRes4 <- rma(ai=A, bi=B, ci=C, di=D, measure="OR", method="DL", add=.5, to=
"only0", data=MindfulDat)
MindfulRes4
MindfulRes5 <- rma(ai=A, bi=B, ci=C, di=D, measure="OR", method="REML", add=.5, to=
"only0", data=MindfulDat)
MindfulRes5
MindfulRes6 <- rma(ai=A, bi=B, ci=C, di=D, measure="OR", method="FE", add=.5, to=
"only0", data=MindfulDat)
MindfulRes6

####Tutorial 9: Basic Confidence and Prediction Interval####


# Loading library (if not yet)
library(metafor)
library(xlsx)

# Formula for Higgins Prediction Interval


Higgins.PI <- function(M, Tausq, SEM, k){
df1 <- k-2
SEPI <- sqrt(Tausq+SEM^2)
PI.lb <- M-qt(.975, df=df1)*SEPI
PI.ub <- M+qt(.975, df=df1)*SEPI
cbind(PI.lb,PI.ub)
}

# Read excel data, print it


McNattDat <- read.xlsx("C:\\Users\\Surya Dila\\Desktop\\R tutorial M
Brannick\\9\\McNattData.xlsx", sheetName="Data")
McNattDat

# Generic Meta Analysis, print it


McNattRes1 <- rma(yi=d, vi=v, method="DL", data=McNattDat)
McNattRes1

# Confidence Interval for REVC (Random Effect Variance Component)


confint(McNattRes1)

# Prediction Interval for REVC


predict(McNattRes1)

# Prediction Interval using Higgins formula (input the value based on the results
of previous analysis)
Higgins.PI(M, tausq, SEM, k)

####Tutorial 10: Moderators Analysis (categorical/continues covariates)####


# Load libray (if not yet)
library(metafor)
library(xlsx)

# Read excel data, print it


McLeodDat <- read.xlsx("C:\\Users\\Surya Dila\\Desktop\\R tutorial M Brannick\\3
and 10\\McLeod2007.xlsx", sheetName = "Data")
McLeodDat

# Meta analysis, print it


# Correlation
McLeodRes1 <- rma(ni=N, ri=r, method="DL", measure="ZCOR", data=McLeodDat)
McLeodRes1
# Introducing categorical covariate, Dx to the model [the R code for categorical
covariate >> factor()]
McLeodRes2 <- rma(ni=N, ri=r, method="DL", measure="ZCOR", mods = ~factor(Dx),
data=McLeodDat)
McLeodRes2
# Knapp and Hartung adjustment
McLeodRes3 <- rma(ni=N, ri=r, method="DL", measure="ZCOR", mods = ~factor(Dx),
knha=TRUE, data=McLeodDat)
McLeodRes3

# Omnibus test for moderator (analog to R-square in regression)


McLeodRes4 <- rma(ni=N, ri=r, method="DL", measure="ZCOR", mods = ~factor(Dx)-1,
knha=TRUE, data=McLeodDat)
McLeodRes4

# Introducing continues covariate, Age (no need special identification in writing R


code)
McLeodRes5 <- rma(ni=N, ri=r, method="DL", measure="ZCOR", mods=~factor(Dx)+Age,
data=McLeodDat)
McLeodRes5

####Tutorial 11: Funnel Plots (ordinary, trim and fill)####


# Load library, (if not yet)
library(metafor)
library(xlsx)

# Read, print McDaniel data (I think it is with McLeod data, so load McLeod excel
file first if below code is not work)
dat.mcdaniel1994

# Meta Analysis of Correlation using ZCOR, print it


McDanielRes1 <- rma(ri=ri, ni=ni, method="DL", measure="ZCOR",
data=dat.mcdaniel1994)
McDanielRes1

# Ordinary funnel plot & prediction interval


funnel(McDanielRes1)
predict(McDanielRes1)

# Trim and fill, trim & fill funnel plot, prediction interval, print it
McDanielRes2 <- trimfill(McDanielRes1)
McDanielRes2
funnel(McDanielRes2)
predict(McDanielRes2)

# Meta Analysis of Correlation using COR, print it


McDanielRes3 <- rma(ri=ri, ni=ni, method="DL", measure="COR",
data=dat.mcdaniel1994)
McDanielRes3
funnel(McDanielRes3)
predict(McDanielRes3)
# Notice that the funnel plot indicating more heterogeneity
McDanielRes4 <- trimfill(McDanielRes3)
McDanielRes4
funnel(McDanielRes4)
predict(McDanielRes4)

####Tutorial 12: Forest Plots (Ordinary, sorted, moderator, cumulative)####


# Load library (if not yet)
library(metafor)
library(xlsx)

# EXAMPLE 1
# Load excel file of McLeod, print it
McLeodDat <- read.xlsx("C:\\Users\\Surya Dila\\Desktop\\R tutorial M Brannick\\3
and 10\\McLeod2007.xlsx", sheetName = "Data")
McLeodDat

# Meta analysis, print it


McLeodRes1 <- rma(ni=N, ri=r, method="DL", measure="ZCOR", data=McLeodDat)
McLeodRes1

# Forest plot (unsorted format)


forest(McLeodRes1)

# Sorted by the effect size (in this case, r), print it


McLeod.ES <- McLeodDat[order(McLeodDat$r), ]
McLeod.ES

# Meta analysis with ordered data, print it (overall summary should be the same, of
course!)
McLeodRes2 <- rma(ni=N, ri=r, method="DL", measure="ZCOR", data=McLeod.ES)
McLeodRes2

# Forest plot (sorted by r)


forest(McLeodRes2)

# Sorted by precision (v)


McLeod.V <- McLeodDat[order(McLeodDat$v), ]
McLeod.V
McLeodRes3 <- rma(ri=r, ni=N, method="DL", measure="ZCOR", data=McLeod.V)
forest(McLeodRes3)

# Sorted by moderator (example, Dx), then ES, test for moderator; forest by
moderator & ES (with test results)
McLeod.Dx.ES <- McLeodDat[order(McLeodDat$Dx,McLeodDat$r), ]
McLeodRes4 <- rma(ni=N, ri=r, method="DL", measure="ZCOR", mods=~factor(Dx),
data=McLeod.ES)
McLeodRes4

# EXAMPLE 2
# Load libray (if not yet)
library(metafor)
library(xlsx)

# Load Rock excel file, print it


RocksDat <- read.xlsx("C:\\Users\\Surya Dila\\Desktop\\CMA workshop-Michael
Brannick\\Datasets example\\RocksLMX_CC-2.xlsx", sheetName="Data")
RocksDat

# Sorted by date (year)


RocksDat.Yr <- RocksDat[order(RocksDat$Year), ]
RocksRes1 <- rma(ni=N, ri=r, method="DL", measure="ZCOR", data=RocksDat.Yr)
RocksRes1
forest(RocksRes1)

# Cummulative analysis
RocksCU <- cumul(RocksRes1, order = order(RocksDat.Yr$Year))
forest(RocksCU)

####Tutorial 13: Forest Plots (Making them look the way you want)####
##I use McLeod data (correlation) instead of sleep data (binary) because I don't
have it. But I just want to practice how to labeling the forest plot##
# Load library (if not yet loaded)
library(metafor)
library(xlsx)

# Load excel file


McLeodDat <- McLeodDat <- read.xlsx("C:\\Users\\Surya Dila\\Desktop\\R tutorial M
Brannick\\3 and 10\\McLeod2007.xlsx", sheetName = "Data")
McLeodDat

# Sort by Dx and r
McLeod.Dx.r <- McLeodDat[order(McLeodDat$Dx,McLeodDat$r), ]
McLeod.Dx.r

# Meta analysis
McLeodRes1 <- rma(ri=r, ni=N, method="DL", measure="ZCOR", data=McLeod.Dx.r)
McLeodRes1

# Forest plot
forest(McLeodRes1)

# Meta analysis of subset of study (example, study 38 till 45)


McLeodDat.8 <- McLeod.Dx.r[38:45, ]
McLeodRes8 <- rma(ri=r, ni=N, method="DL", measure="ZCOR", data=McLeodDat.8)

# Meta analysis and forest plot introducing categorical moderator (Parent)


McLeodRes8 <- rma(ri=r, ni=N, method="DL", measure="ZCOR", mods = ~factor(Parent),
data=McLeodDat.8)
forest(McLeodRes8)

# Writing study labels (slab=...)


forest(McLeodRes8, slab=paste(McLeodDat.8$Author, McLeodDat.8$Year, sep=", "))
# Converting to odds from log(odds) (atransf=...)
forest(McLeodRes8, slab=paste(McLeodDat.8$Author, McLeodDat.8$Year, sep=", "),
atransf=exp)

# Change ES label from "Fisher z Tranformed Correlation Coefficient" to "Odds


Ratio" (xlab=...)
forest(McLeodRes8, slab=paste(McLeodDat.8$Author, McLeodDat.8$Year, sep=", "),
atransf=exp, xlab="Odds Ratio")

# Make scales the same for both side of graphs (at=...)


forest(McLeodRes8, slab=paste(McLeodDat.8$Author, McLeodDat.8$Year, sep=", "),
atransf=exp, xlab="Odds Ratio", at=c(-1.5, 0, 1.5, 3))

# Add labels to top of figure


forest(McLeodRes8, atransf=exp, xlab="Odds Ratio", slab=paste(McLeodDat.8$Author,
McLeodDat.8$Year, sep=", "), xlim=c(-6,6), at=c(-1.5, 0, 1.5, 3))
op <- par(cex=1, font=2)
text (4.5, 10, "OR [95% CI]")
text (-4.5, 10, "Author, Date")
title("Figure 1: Forest Plot of McLeod Data")
par(op)

# Add labels to top of figure (alternative 1)


forest(McLeodRes8, atransf=exp, xlab="Odds Ratio", slab=paste(McLeodDat.8$Author,
McLeodDat.8$Year, sep=", "), xlim=c(-16,6), at=c(-1.5, 0, 1.5, 3))
text (4, 10, "OR [95% CI]", pos=2, font=2, cex=.9)
text (-15, 10, "Author, Date", pos=4, font=2, cex=.9)
title("Figure 1: Forest Plot of McLeod Data")
# Note: you have to play with the numbers for placement: trial and error; start
with zero, zero to be in the graph and work from there

# Add labels (example by Wolfgang Viechtbauer)


library(metafor)

data(dat.bcg)
dat <- dat.bcg

res <- rma(ai=tpos, bi=tneg, ci=cpos, di=cneg, data=dat, measure="RR")

windows(width=6.5, height=4.0, pointsize=10)


par(mar=c(4,0,4,0))
forest(res, slab=paste(dat$author, ", ", dat$year, sep=""),
xlim=c(-16,6), at=log(c(.05,.25,1,4)), atransf=exp,
ilab=cbind(dat$tpos, dat$tneg, dat$cpos, dat$cneg),
ilab.xpos=c(-9.5,-8,-6,-4.5), cex=.8, ylim=c(-1.5,16), efac=1.8)
text(c(-9.5,-8,-6,-4.5), 14.7, c("TB+", "TB-", "TB+", "TB-"), font=2, cex=.8)
text(c(-8.75,-5.25), 15.7, c("Vaccinated", "Control"), font=2, cex=.8)
text(-16, 14.7, "Author(s) and Year", pos=4, font=2, cex=.8)
text(6, 14.7, "Relative Risk [95% CI]", pos=2, font=2, cex=.8)
title("Figure 1: Forest Plot of the BCG Vaccine Data")

########################################################################
# So, just use the text() function to add those column headings. With the ilab and
ilab.xpos arguments, you can add the information for those columns to the plot.
# Note: you have to play with the numbers for placement: trial and error; start
with zero, zero to be in the graph and work from there

#### Errors I have been faced ####


- Note that R is case sensitive
- Error in loading excel file using library(xlsx) >> (1) you must include the local
disk, for example: C: or D:; (2) use "\\" or / to separate the path; (3) check the
correct path of your file by right click the excel file, choose properties, then
copy the path, add your file name; (4) file name should exactly the same containing
.xlsx
- Error in forest.default(McLeodDat.8, slab = paste(McLeodDat.8$Author, :
Must specify either 'vi', 'sei', or ('ci.lb', 'ci.ub') pairs. >> forest(...) must
be contain study result, not study data. I write McLeodDat.8 instead McLeodRes8
- text (coordinate X, coordinate Y) >> minus sign (-) to move the text to left,
plus sign (+) to move the text to right (X). Minus and plus sign (- and +) to move
down and up the text respectively (Y)
- font=... >> 1 is ordinary text, 2 is bold (the most commonly used), 3 is italic,
4 is italic & bold, and soon (check with your own trial and error)
- cex=...(decimal number) >> I think this is determine font size, .8 or .9 is the
largest size? I found it after trial and error of some numbers, I don't know
exactly
- pos=... >> I think it is "position", I found these combination (pos=2 for ES
title, pos=4 for study title) the most perfect one
- xlim (-x, x) >> to adjust the position of the forest, the scale and the label
below it (in horizontal axis)
- Begin with op <- par(cex=..., font=..), provide the text coordinates and finish
with par(op) >> to add text whereever you want in the forest plot (depend on
coordinates you give, a trial and error attempts)
- ilab.xpos=... >> There are 4 coordinates, all determine the x location of "event-
non event" of intervention and "event-non event" of control group
- ylim=c(-y, y) >> to adjust the content of forest plot position in vertical axis

####Trial for S 103 ORC####


# Load library
library(metafor)
library(xlsx)

# Load excel files that already organized as preferred input format, print it
S103Dat <- read.xlsx("D:\\Researcher\\Online Research Club (SuryaDila)\\Study
103\\Task 14 (data extraction period 1)\\Full assignments results\\Study 103 Data
Extraction\\Data extraction results\\Study 103.xlsx", sheetName="1-4mo")
S103Dat

# Meta analysis (first try random effect, to look the heterogeneity)


S103Res1 <- rma(ai=TEvent, bi=TNonEvent, ci=CEvent, di=CNonEvent, method="DL",
measure="OR", data=S103Dat)
S103Res1

# Because no heterogeneity, so I tried fixed effect meta analysis


S103Res1 <- rma(ai=TEvent, bi=TNonEvent, ci=CEvent, di=CNonEvent, method="FE",
measure="OR", data=S103Dat)
S103Res1

# Try looking the forest plot


S103Res1 <- rma(ai=TEvent, bi=TNonEvent, ci=CEvent, di=CNonEvent, method="FE",
measure="OR", data=S103Dat)
forest(S103Res1, atransf=exp, xlab="Odds Ratio",
slab=paste(S103Dat$Author,S103Dat$Year, sep=", "), at=c(-1.5, 0, 1.5, 3))

# Trial forest
# decrease margins so the full space is used
par(mar=c(4,4,1,2))
# fit fixed-effect model (use slab argument to define study labels)
S103Res1 <- rma(ai=TEvent, bi=TNonEvent, ci=CEvent, di=CNonEvent, data=S103Dat,
measure="OR",
slab=paste(Author, Year, sep=", "), method="FE")

### set up forest plot (with 2x2 table counts added; rows argument is used
### to specify exactly in which rows the outcomes will be plotted)
forest(S103Res1, xlim=c(-16, 6), at=log(c(0.05, 0.25, 1, 4)), atransf=exp,
ilab=cbind(S103Dat$TEvent, S103Dat$TNonEvent, S103Dat$CEvent,
S103Dat$CNonEvent),
ilab.xpos=c(-9.5,-8,-6,-4.5), cex=0.75, ylim=c(-8, 8),
xlab="Odds Ratio", mlab="", psize=1)

### add text with Q-value, dfs, p-value, and I^2 statistic
text(-16, -1, pos=4, cex=0.75, bquote(paste("FE Model (Q = ",
.(formatC(S103Res1$QE, digits=2, format="f")), ", df = ", .(S103Res1$k -
S103Res1$p),
", p = ", .(formatC(S103Res1$QEp, digits=2, format="f")), "; ", I^2, " = ",
.(formatC(S103Res1$I2, digits=1, format="f")), "%)")))

### set font expansion factor (as in forest() above) and use bold italic
### font and save original settings in object 'op'
op <- par(cex=0.75, font=4)

### switch to bold font


par(font=2)

### add column headings to the plot


text(c(-9.5,-8,-6,-4.5), 6.5, c("Malaria+", "Malaria-", "Malaria+", "Malaria-"))
text(c(-8.75,-5.25), 7.5, c("Treated", "Control"))
text(-16, 6.5, "Author(s) and Year", pos=4)
text(6, 6.5, "Odds Ratio [95% CI]", pos=2)

### set par back to the original settings


par(op)

###################################################################################
################
windows(width=6.5, height=4.0, pointsize=10)
par(mar=c(4,0,4,0))
forest(S103Res1, slab=paste(S103Dat$Author, ", ", S103Dat$Year, sep=""),
xlim=c(-16,6), at=log(c(.05,.25,1,4)), atransf=exp,
ilab=cbind(S103Dat$tmalaria, S103Dat$tnomalaria, S103Dat$cmalaria,
S103Dat$cnomalaria),
ilab.xpos=c(-9.5,-8,-6,-4.5), cex=.8, ylim=c(-1.5,16), efac=1.8)
text(c(-9.5,-8,-6,-4.5), 14.7, c("malaria+", "malaria-", "malaria+", "malaria-"),
font=2, cex=.8)
text(c(-8.75,-5.25), 15.7, c("Treated", "Control"), font=2, cex=.8)
text(-16, 14.7, "Author(s) and Year", pos=4, font=2, cex=.8)
text(6, 14.7, "Odds Ratio [95% CI]", pos=2, font=2, cex=.8)
###################################################################################
#################

#### Example R code for forest plot with subgroups ####


library(metafor)

### decrease margins so the full space is used


par(mar=c(4,4,1,2))
### fit random-effects model (use slab argument to define study labels)
res <- rma(ai=tpos, bi=tneg, ci=cpos, di=cneg, data=dat.bcg, measure="RR",
slab=paste(author, year, sep=", "), method="REML")

### set up forest plot (with 2x2 table counts added; rows argument is used
### to specify exactly in which rows the outcomes will be plotted)
forest(res, xlim=c(-16, 6), at=log(c(0.05, 0.25, 1, 4)), atransf=exp,
ilab=cbind(dat.bcg$tpos, dat.bcg$tneg, dat.bcg$cpos, dat.bcg$cneg),
ilab.xpos=c(-9.5,-8,-6,-4.5), cex=0.75, ylim=c(-1, 27),
order=order(dat.bcg$alloc), rows=c(3:4,9:15,20:23),
xlab="Risk Ratio", mlab="", psize=1)

### add text with Q-value, dfs, p-value, and I^2 statistic
text(-16, -1, pos=4, cex=0.75, bquote(paste("RE Model for All Studies (Q = ",
.(formatC(res$QE, digits=2, format="f")), ", df = ", .(res$k - res$p),
", p = ", .(formatC(res$QEp, digits=2, format="f")), "; ", I^2, " = ",
.(formatC(res$I2, digits=1, format="f")), "%)")))

### set font expansion factor (as in forest() above) and use bold italic
### font and save original settings in object 'op'
op <- par(cex=0.75, font=4)

### add text for the subgroups


text(-16, c(24,16,5), pos=4, c("Systematic Allocation",
"Random Allocation",
"Alternate Allocation"))

### switch to bold font


par(font=2)

### add column headings to the plot


text(c(-9.5,-8,-6,-4.5), 26, c("TB+", "TB-", "TB+", "TB-"))
text(c(-8.75,-5.25), 27, c("Vaccinated", "Control"))
text(-16, 26, "Author(s) and Year", pos=4)
text(6, 26, "Risk Ratio [95% CI]", pos=2)

### set par back to the original settings


par(op)

### fit random-effects model in the three subgroups


res.s <- rma(ai=tpos, bi=tneg, ci=cpos, di=cneg, data=dat.bcg, measure="RR",
subset=(alloc=="systematic"), method="REML")
res.r <- rma(ai=tpos, bi=tneg, ci=cpos, di=cneg, data=dat.bcg, measure="RR",
subset=(alloc=="random"), method="REML")
res.a <- rma(ai=tpos, bi=tneg, ci=cpos, di=cneg, data=dat.bcg, measure="RR",
subset=(alloc=="alternate"), method="REML")

### add summary polygons for the three subgroups


addpoly(res.s, row=18.5, cex=0.75, atransf=exp, mlab="")
addpoly(res.r, row= 7.5, cex=0.75, atransf=exp, mlab="")
addpoly(res.a, row= 1.5, cex=0.75, atransf=exp, mlab="")

### add text with Q-value, dfs, p-value, and I^2 statistic for subgroups
text(-16, 18.5, pos=4, cex=0.75, bquote(paste("RE Model for Subgroup (Q = ",
.(formatC(res.s$QE, digits=2, format="f")), ", df = ", .(res.s$k - res.s$p),
", p = ", .(formatC(res.s$QEp, digits=2, format="f")), "; ", I^2, " = ",
.(formatC(res.s$I2, digits=1, format="f")), "%)")))
text(-16, 7.5, pos=4, cex=0.75, bquote(paste("RE Model for Subgroup (Q = ",
.(formatC(res.r$QE, digits=2, format="f")), ", df = ", .(res.r$k - res.r$p),
", p = ", .(formatC(res.r$QEp, digits=2, format="f")), "; ", I^2, " = ",
.(formatC(res.r$I2, digits=1, format="f")), "%)")))
text(-16, 1.5, pos=4, cex=0.75, bquote(paste("RE Model for Subgroup (Q = ",
.(formatC(res.a$QE, digits=2, format="f")), ", df = ", .(res.a$k - res.a$p),
", p = ", .(formatC(res.a$QEp, digits=2, format="f")), "; ", I^2, " = ",
.(formatC(res.a$I2, digits=1, format="f")), "%)")))

ADVANCED R E-book: http://adv-r.had.co.nz/

You might also like