metafor
PackageÂ
metafor
package (Viechtbauer,
2010)Â
# install the metafor package
install.packages("metafor")
# load the metafor package
library(metafor)
# for the example data, we also need the bayesmeta package
install.packages("bayesmeta")
Â
Â
Â
escalc()
allows for the calculation of 70+ different types of effect size
measures / outcome measures# copy the data to 'dat'
dat <- get(data(KarnerEtAl2014, package="bayesmeta"))
# examine the dataset (for a selection of variables)
dat[c(1,10:11,17:18)]
# study tiotropium.total tiotropium.exa placebo.total placebo.exa
# 1 Bateman2010a 1989 685 2002 842
# 2 Bateman2010b 1337 495 653 288
# 3 Beeh2006 1236 180 403 80
# 4 Brusasco2003 402 129 400 156
# 5 Casaburi2002 550 198 371 156
# 6 Chan2007 608 268 305 125
# 7 Cooper2010 260 112 259 102
# 8 Covelli2005 100 9 96 12
# 9 Dusser2006 500 213 510 272
# 10 Freeman2007 200 19 195 35
# 11 Johansson2008 107 2 117 4
# 12 Magnussen2008 228 13 244 26
# 13 Moita2008 147 6 164 6
# 14 NCT00144326 123 11 127 12
# 15 Niewoehner2005 914 255 915 296
# 16 Powrie2007 69 30 73 47
# 17 Sun2007 30 0 30 2
# 18 Tashkin2008 2987 2001 3006 2049
# 19 Tonnel2008 266 101 288 130
# 20 Trooster2011 238 11 219 24
# 21 Verkindre2006 46 10 54 8
# 22 Voshaar2008 360 43 181 21
# tiotropium.total - total number of patients in the treatment group
# tiotropium.exa - patients with ≥1 exacerbations in the treatment group
# placebo.total - total number of patients in the placebo group
# placebo.exa - patients with ≥1 exacerbations in the placebo group
# calculate log odds ratios and corresponding sampling variances
dat <- escalc(measure="OR", ai=tiotropium.exa, n1i=tiotropium.total,
ci=placebo.exa, n2i=placebo.total,
slab=study, data=dat)
# examine the dataset
cbind(dat[1], "..."="...", dat[28:29])
# study ... yi vi
# 1 Bateman2010a ... -0.3234 0.0043
# 2 Bateman2010b ... -0.2943 0.0094
# 3 Beeh2006 ... -0.3737 0.0221
# 4 Brusasco2003 ... -0.3023 0.0219
# 5 Casaburi2002 ... -0.2546 0.0190
# 6 Chan2007 ... 0.1267 0.0202
# 7 Cooper2010 ... 0.1526 0.0319
# 8 Covelli2005 ... -0.3677 0.2173
# 9 Dusser2006 ... -0.4317 0.0161
# 10 Freeman2007 ... -0.7342 0.0930
# 11 Johansson2008 ... -0.6197 0.7684
# 12 Magnussen2008 ... -0.6793 0.1246
# 13 Moita2008 ... 0.1138 0.3468
# 14 NCT00144326 ... -0.0606 0.1919
# 15 Niewoehner2005 ... -0.2117 0.0104
# 16 Powrie2007 ... -0.8544 0.1187
# 17 Sun2007 ... -1.6773 2.4679
# 18 Tashkin2008 ... -0.0536 0.0030
# 19 Tonnel2008 ... -0.2958 0.0300
# 20 Trooster2011 ... -0.9321 0.1421
# 21 Verkindre2006 ... 0.4683 0.2745
# 22 Voshaar2008 ... 0.0329 0.0803
Â
yi
– log odds ratios (negative values indicate lower
odds of exacerbations in the treatment group)vi
– corresponding sampling variancesÂ
rma()
is one of the main modeling functions for fitting equal-, fixed-,
random-, and mixed-effects meta-regression models# Random-Effects Model (k = 22; tau^2 estimator: REML)
#
# tau^2 (estimated amount of total heterogeneity): 0.0204 (SE = 0.0150)
# tau (square root of estimated tau^2 value): 0.1427
# I^2 (total heterogeneity / total variability): 48.61%
# H^2 (total variability / sampling variability): 1.95
#
# Test for Heterogeneity:
# Q(df = 21) = 42.4592, p-val = 0.0037
#
# Model Results:
#
# estimate se tval df pval ci.lb ci.ub
# -0.2451 0.0544 -4.5076 21 0.0002 -0.3582 -0.1320 ***
#
# ---
# Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# pred ci.lb ci.ub pi.lb pi.ub
# 0.78 0.70 0.88 0.57 1.08
# forest plot
par(mar=c(5,4,2,2))
forest(res, xlim=c(-5,3.8), atransf=exp, at=log(2^(-4:3)),
addpred=TRUE, digits=c(2L,4L), header=TRUE, top=2, shade=TRUE)
# risk-of-bias ratings extracted from Figure 2 from Karner et al (2014)
rob <- read.table(header=TRUE, text = "
rb.a rb.b rb.c rb.d rb.e rb.f
'+' '+' '+' '+' '+' '+'
'+' '+' '+' '+' '-' '+'
'+' '+' '+' '+' '?' '+'
'+' '+' '+' '+' '?' '+'
'+' '+' '+' '+' '?' '+'
'+' '+' '+' '+' '?' '+'
'+' '+' '+' '+' '-' '+'
'+' '+' '+' '+' '?' '?'
'+' '+' '+' '+' '?' '+'
'+' '+' '+' '+' '?' '+'
'+' '+' '+' '+' '+' '+'
'+' '+' '+' '+' '+' '+'
'+' '+' '+' '+' '+' '+'
'+' '+' '+' '+' '+' '+'
'+' '+' '+' '+' '?' '+'
'+' '+' '+' '+' '?' '+'
'+' '?' '+' '?' '+' '+'
'+' '+' '+' '+' '-' '+'
'+' '+' '+' '+' '?' '+'
'+' '+' '+' '+' '+' '+'
'+' '+' '+' '+' '?' '+'
'+' '+' '+' '+' '+' '+'")
# turn the risk of bias items into factors with levels +, -, and ?
rob <- lapply(rob, factor, levels=c("+", "-", "?"))
# estimated average odds ratio (and 95% CI/PI)
pred <- predict(res, transf=exp, digits=2)
# need the rounded estimate and CI bounds further below
pred <- fmtx(c(pred$pred, pred$ci.lb, pred$ci.ub), 2)
# total number of studies
k <- nrow(dat)
# get the weights and format them as will be used in the forest plot
weights <- paste0(fmtx(weights(res), 1), "%")
# adjust the margins
par(mar=c(10.6,0,2.3,1.3), mgp=c(3,0.4,0), tcl=-0.3)
# forest plot with extra annotations
sav <- forest(res, atransf=exp, at=log(c(0.1, 0.2, 0.5, 1, 2, 5, 10)), xlim=c(-15,5),
xlab="", efac=c(0,1,1), textpos=c(-15,-2.5), lty=c(1,1,0), refline=NA,
ilab=cbind(tiotropium.exa, tiotropium.total, placebo.exa, placebo.total, weights),
ilab.xpos=c(-10.8,-9.6,-8.1,-6.9,-5.2), ilab.pos=2,
cex=0.79, header=c("Study","95% CI"), mlab="")
# add the horizontal line at the top
segments(sav$xlim[1], k+1, sav$xlim[2], k+1)
# add the vertical reference line at 0
segments(0, -3, 0, k+1)
# now we add a bunch of text; since some of the text falls outside of the
# plot region, we set xpd=NA so nothing gets clipped
par(xpd=NA)
# adjust cex as used in the forest plot and use a bold font
par(cex=sav$cex, font=2)
text(sav$ilab.xpos, k+2, pos=2, c("Events","Total","Events","Total","Weight"))
text(c(mean(sav$ilab.xpos[1:2])+0.2,mean(sav$ilab.xpos[3:4])), k+3, pos=2, c("tiotropium","placebo"))
text(sav$textpos[2], k+3, "Odds ratio", pos=2)
text(0, k+3, "Odds ratio")
text(sav$xlim[2]-0.35, k+3, "Risk of Bias", pos=2)
text(0, k+2, "IV, Random, 95% CI")
text(c(sav$xlim[1],sav$ilab.xpos[c(2,4,5)]), -1, pos=c(4,2,2,2,2),
c("Total (95% CI)", sum(dat$tiotropium.total), sum(dat$placebo.total), "100.0%"))
text(sav$xlim[1], -7, pos=4, "Risk of bias legend")
# first hide the non-bold summary estimate text and then add it back in bold font
rect(sav$textpos[2], -1.5, sav$ilab.xpos[5], -0.5, col="white", border=NA)
text(sav$textpos[2], -1, paste0(pred[1], " [", pred[2], ", ", pred[3], "]"), pos=2)
# use a non-bold font for the rest of the text
par(cex=sav$cex, font=1)
# add favours text below the x-axis
text(log(c(1,1)), -4.5, c("Favours tiotropium","Favours placebo"), pos=c(2,4), offset=0.5)
# add text for total events
text(sav$xlim[1], -2, pos=4, "Total events:")
text(sav$ilab.xpos[c(1,3)], -2, c(sum(dat$tiotropium.exa),sum(dat$placebo.exa)), pos=2)
# add text with heterogeneity statistics
text(sav$xlim[1], -3, pos=4, bquote(paste("Heterogeneity: ",
"Tau"^2, " = ", .(fmtx(res$tau2, 2)), "; ",
"Chi"^2, " = ", .(fmtx(res$QE, 2)),
", df = ", .(res$k - res$p),
" (", .(fmtp(res$QEp, digits=3, pname="P", add0=TRUE, sep=TRUE, equal=TRUE)), "); ",
I^2, " = ", .(round(res$I2)), "%")))
# add text for test of overall effect
text(sav$xlim[1], -4, pos=4, bquote(paste("Test for overall effect: Z = ",
.(fmtx(res$zval, 2)), " (", .(fmtp(res$pval, digits=5, pname="P", add0=TRUE, sep=TRUE, equal=TRUE)), ")")))
# add risk of bias points and symbols
cols <- c("#00cc00", "#cc0000", "#eeee00")
syms <- levels(rob$rb.a)
pos <- seq(sav$xlim[2]-2.4,sav$xlim[2]-0.5,length=6)
for (i in 1:6) {
points(rep(pos[i],k), k:1, pch=19, col=cols[rob[[i]]], cex=2.2)
text(pos[i], k:1, syms[rob[[i]]], font=2)
}
text(pos, k+2, c("A","B","C","D","E","F"), font=2)
# add risk of bias legend
text(sav$xlim[1], -8:-13, pos=4, c(
"(A) Random sequence generation (selection bias)",
"(B) Allocation concealment (selection bias)",
"(C) Blinding of participants and personnel (performance bias)",
"(D) Blinding of outcome assessment (detection bias)",
"(E) Incomplete outcome data (attrition bias)",
"(F) Selective reporting (reporting bias)"))