This vignette provides up-to-date commands for the analyses in “How to perform a meta-analysis with R: a practical tutorial”, Evid Based Ment Health (Balduzzi, Rücker, and Schwarzer 2019).
library(meta)
#> Loading required package: metadat
#> Loading 'meta' package (version 8.0-2).
#> Type 'help(meta)' for a brief overview.
Note, a similar message would be printed for R package metasens. However, this vignette does not actually load metasens as it might not be installed in addition to meta.
Print results with two significant digits and use Paule-Mandel estimator for between-study variance.
Note, in the publication, argument ‘method.tau’ was used in R function metabin(). Here, we set the Paule-Mandel method as the default for any meta-analysis conducted in the current R session.
joy = read.csv("Joy2006.txt")
# Add new variable: miss
joy$miss = ifelse((joy$drop.h + joy$drop.p) == 0,
"Without missing data", "With missing data")
head(joy)
#> author year resp.h fail.h drop.h resp.p fail.p drop.p miss
#> 1 Arvanitis 1997 25 25 2 18 33 0 With missing data
#> 2 Beasley 1996 29 18 22 20 14 34 With missing data
#> 3 Bechelli 1983 12 17 1 2 28 1 With missing data
#> 4 Borison 1992 3 9 0 0 12 0 Without missing data
#> 5 Chouinard 1993 10 11 0 3 19 0 Without missing data
#> 6 Durost 1964 11 8 0 1 14 0 Without missing data
str(joy)
#> 'data.frame': 17 obs. of 9 variables:
#> $ author: chr "Arvanitis" "Beasley" "Bechelli" "Borison" ...
#> $ year : int 1997 1996 1983 1992 1993 1964 1962 1974 1994 1982 ...
#> $ resp.h: int 25 29 12 3 10 11 7 8 19 1 ...
#> $ fail.h: int 25 18 17 9 11 8 18 9 45 9 ...
#> $ drop.h: int 2 22 1 0 0 0 1 0 2 0 ...
#> $ resp.p: int 18 20 2 0 3 1 4 3 14 0 ...
#> $ fail.p: int 33 14 28 12 19 14 21 10 50 10 ...
#> $ drop.p: int 0 34 1 0 0 0 1 0 2 0 ...
#> $ miss : chr "With missing data" "With missing data" "With missing data" "Without missing data" ...
m.publ = metabin(resp.h, resp.h + fail.h, resp.p, resp.p + fail.p,
data = joy, studlab = paste0(author, " (", year, ")"),
label.e = "Haloperidol", label.c = "Placebo",
label.left = "Favours placebo", label.right = "Favours haloperidol")
Print results of meta-analysis (Figure 1).
summary(m.publ)
#> RR 95%-CI %W(common) %W(random)
#> Arvanitis (1997) 1.42 [0.89; 2.25] 21.1 14.1
#> Beasley (1996) 1.05 [0.73; 1.50] 27.5 15.6
#> Bechelli (1983) 6.21 [1.52; 25.35] 2.3 4.7
#> Borison (1992) 7.00 [0.40; 121.94] 0.6 1.4
#> Chouinard (1993) 3.49 [1.11; 10.95] 3.5 6.3
#> Durost (1964) 8.68 [1.26; 59.95] 1.3 2.8
#> Garry (1962) 1.75 [0.58; 5.24] 4.7 6.7
#> Howard (1974) 2.04 [0.67; 6.21] 4.0 6.6
#> Marder (1994) 1.36 [0.75; 2.47] 16.6 12.2
#> Nishikawa (1982) 3.00 [0.14; 65.55] 0.6 1.2
#> Nishikawa (1984) 9.00 [0.57; 142.29] 0.8 1.5
#> Reschke (1974) 3.79 [1.06; 13.60] 3.4 5.4
#> Selman (1976) 1.48 [0.94; 2.35] 10.3 14.1
#> Serafetinides (1972) 8.38 [0.50; 141.44] 0.6 1.4
#> Simpson (1967) 2.27 [0.12; 41.77] 0.8 1.4
#> Spencer (1992) 11.00 [1.67; 72.40] 1.2 3.0
#> Vichaiya (1971) 19.00 [1.16; 311.71] 0.6 1.5
#>
#> Number of studies: k = 17
#> Number of observations: o = 818 (o.e = 446, o.c = 372)
#> Number of events: e = 274
#>
#> RR 95%-CI z p-value
#> Common effect model 2.09 [1.69; 2.59] 6.71 < 0.0001
#> Random effects model 2.15 [1.51; 3.06] 4.23 < 0.0001
#>
#> Quantifying heterogeneity (with 95%-CIs):
#> tau^2 = 0.1754 [0.0000; 1.0088]; tau = 0.4188 [0.0000; 1.0044]
#> I^2 = 41.3% [0.0%; 67.0%]; H = 1.30 [1.00; 1.74]
#>
#> Test of heterogeneity:
#> Q d.f. p-value
#> 27.24 16 0.0388
#>
#> Details of meta-analysis methods:
#> - Mantel-Haenszel method (common effect model)
#> - Inverse variance method (random effects model)
#> - Paule-Mandel estimator for tau^2
#> - Q-Profile method for confidence interval of tau^2 and tau
#> - Calculation of I^2 based on Q
#> - Continuity correction of 0.5 in studies with zero cell frequencies
Same printout (result not shown)
Create Figure 2 (file ‘figure2.pdf’).
m.publ.sub = update(m.publ, subgroup = miss, print.subgroup.name = FALSE)
m.publ.sub
#> Number of studies: k = 17
#> Number of observations: o = 818 (o.e = 446, o.c = 372)
#> Number of events: e = 274
#>
#> RR 95%-CI z p-value
#> Common effect model 2.09 [1.69; 2.59] 6.71 < 0.0001
#> Random effects model 2.15 [1.51; 3.06] 4.23 < 0.0001
#>
#> Quantifying heterogeneity (with 95%-CIs):
#> tau^2 = 0.1754 [0.0000; 1.0088]; tau = 0.4188 [0.0000; 1.0044]
#> I^2 = 41.3% [0.0%; 67.0%]; H = 1.30 [1.00; 1.74]
#>
#> Test of heterogeneity:
#> Q d.f. p-value
#> 27.24 16 0.0388
#>
#> Results for subgroups (common effect model):
#> k RR 95%-CI Q I^2
#> With missing data 10 1.70 [1.35; 2.14] 13.73 34.4%
#> Without missing data 7 4.36 [2.45; 7.77] 3.35 0.0%
#>
#> Test for subgroup differences (common effect model):
#> Q d.f. p-value
#> Between groups 8.82 1 0.0030
#>
#> Results for subgroups (random effects model):
#> k RR 95%-CI tau^2 tau
#> With missing data 10 1.71 [1.13; 2.59] 0.1785 0.4224
#> Without missing data 7 3.80 [2.13; 6.80] 0 0
#>
#> Test for subgroup differences (random effects model):
#> Q d.f. p-value
#> Between groups 4.80 1 0.0285
#>
#> Details of meta-analysis methods:
#> - Mantel-Haenszel method (common effect model)
#> - Inverse variance method (random effects model)
#> - Paule-Mandel estimator for tau^2
#> - Q-Profile method for confidence interval of tau^2 and tau
#> - Calculation of I^2 based on Q
#> - Continuity correction of 0.5 in studies with zero cell frequencies
Create Figure 3 (file ‘figure3.pdf’).
# Impute as no events (ICA-0) - default
mmiss.0 = metamiss(m.publ, drop.h, drop.p)
# Impute as events (ICA-1)
mmiss.1 = metamiss(m.publ, drop.h, drop.p, method = "1")
# Observed risk in control group (ICA-pc)
mmiss.pc = metamiss(m.publ, drop.h, drop.p, method = "pc")
# Observed risk in experimental group (ICA-pe)
mmiss.pe = metamiss(m.publ, drop.h, drop.p, method = "pe")
# Observed group-specific risks (ICA-p)
mmiss.p = metamiss(m.publ, drop.h, drop.p, method = "p")
# Best-case scenario (ICA-b)
mmiss.b = metamiss(m.publ, drop.h, drop.p, method = "b", small.values = "bad")
# Worst-case scenario (ICA-w)
mmiss.w = metamiss(m.publ, drop.h, drop.p, method = "w", small.values = "bad")
# Gamble-Hollis method
mmiss.gh = metamiss(m.publ, drop.h, drop.p, method = "GH")
# IMOR.e = 2 and IMOR.c = 2 (same as available case analysis)
mmiss.imor2 = metamiss(m.publ, drop.h, drop.p, method = "IMOR", IMOR.e = 2)
# IMOR.e = 0.5 and IMOR.c = 0.5
mmiss.imor0.5 = metamiss(m.publ, drop.h, drop.p, method = "IMOR", IMOR.e = 0.5)
Summarise results using R function metabind().
meths = c("Available case analysis (ACA)",
"Impute no events (ICA-0)", "Impute events (ICA-1)",
"Observed risk in control group (ICA-pc)",
"Observed risk in experimental group (ICA-pe)",
"Observed group-specific risks (ICA-p)",
"Best-case scenario (ICA-b)", "Worst-case scenario (ICA-w)",
"Gamble-Hollis analysis",
"IMOR.e = 2, IMOR.c = 2", "IMOR.e = 0.5, IMOR.c = 0.5")
# Use inverse-variance method for pooling (which is used for
# imputation methods)
m.publ.iv = update(m.publ, method = "Inverse")
# Combine results (random effects)
mbr = metabind(m.publ.iv,
mmiss.0, mmiss.1,
mmiss.pc, mmiss.pe, mmiss.p,
mmiss.b, mmiss.w, mmiss.gh,
mmiss.imor2, mmiss.imor0.5,
name = meths, pooled = "random")
Create Figure 4 (file ‘figure4.pdf’).
metabias(m.publ, method.bias = "score")
#> Linear regression test of funnel plot asymmetry
#>
#> Test result: t = 4.56, df = 15, p-value = 0.0004
#> Bias estimate: 2.21 (SE = 0.4853)
#>
#> Details:
#> - multiplicative residual heterogeneity variance (tau^2 = 1.0948)
#> - predictor: standard error of score
#> - weight: inverse variance of score
#> - reference: Harbord et al. (2006), Stat Med
tf.publ = trimfill(m.publ)
tf.publ
#> Number of studies: k = 26 (with 9 added studies)
#> Number of observations: o = 1174 (o.e = 645, o.c = 529)
#> Number of events: e = 374
#>
#> RR 95%-CI z p-value
#> Random effects model 1.40 [0.83; 2.38] 1.26 0.2063
#>
#> Quantifying heterogeneity (with 95%-CIs):
#> tau^2 = 1.0983 [0.2929; 3.1894]; tau = 1.0480 [0.5412; 1.7859]
#> I^2 = 56.2% [32.1%; 71.8%]; H = 1.51 [1.21; 1.88]
#>
#> Test of heterogeneity:
#> Q d.f. p-value
#> 57.13 25 0.0003
#>
#> Details of meta-analysis methods:
#> - Inverse variance method
#> - Paule-Mandel estimator for tau^2
#> - Q-Profile method for confidence interval of tau^2 and tau
#> - Calculation of I^2 based on Q
#> - Trim-and-fill method to adjust for funnel plot asymmetry (L-estimator)
summary(tf.publ)
#> RR 95%-CI %W(random)
#> Arvanitis (1997) 1.42 [0.89; 2.25] 6.2
#> Beasley (1996) 1.05 [0.73; 1.50] 6.4
#> Bechelli (1983) 6.21 [1.52; 25.35] 4.5
#> Borison (1992) 7.00 [0.40; 121.94] 2.2
#> Chouinard (1993) 3.49 [1.11; 10.95] 5.0
#> Durost (1964) 8.68 [1.26; 59.95] 3.5
#> Garry (1962) 1.75 [0.58; 5.24] 5.1
#> Howard (1974) 2.04 [0.67; 6.21] 5.1
#> Marder (1994) 1.36 [0.75; 2.47] 6.0
#> Nishikawa (1982) 3.00 [0.14; 65.55] 2.0
#> Nishikawa (1984) 9.00 [0.57; 142.29] 2.3
#> Reschke (1974) 3.79 [1.06; 13.60] 4.7
#> Selman (1976) 1.48 [0.94; 2.35] 6.2
#> Serafetinides (1972) 8.38 [0.50; 141.44] 2.3
#> Simpson (1967) 2.27 [0.12; 41.77] 2.2
#> Spencer (1992) 11.00 [1.67; 72.40] 3.6
#> Vichaiya (1971) 19.00 [1.16; 311.71] 2.3
#> Filled: Chouinard (1993) 0.50 [0.16; 1.55] 5.0
#> Filled: Reschke (1974) 0.46 [0.13; 1.64] 4.7
#> Filled: Bechelli (1983) 0.28 [0.07; 1.14] 4.5
#> Filled: Borison (1992) 0.25 [0.01; 4.31] 2.2
#> Filled: Serafetinides (1972) 0.21 [0.01; 3.49] 2.3
#> Filled: Durost (1964) 0.20 [0.03; 1.38] 3.5
#> Filled: Nishikawa (1984) 0.19 [0.01; 3.04] 2.3
#> Filled: Spencer (1992) 0.16 [0.02; 1.04] 3.6
#> Filled: Vichaiya (1971) 0.09 [0.01; 1.49] 2.3
#>
#> Number of studies: k = 26 (with 9 added studies)
#> Number of observations: o = 1174 (o.e = 645, o.c = 529)
#> Number of events: e = 374
#>
#> RR 95%-CI z p-value
#> Random effects model 1.40 [0.83; 2.38] 1.26 0.2063
#>
#> Quantifying heterogeneity (with 95%-CIs):
#> tau^2 = 1.0983 [0.2929; 3.1894]; tau = 1.0480 [0.5412; 1.7859]
#> I^2 = 56.2% [32.1%; 71.8%]; H = 1.51 [1.21; 1.88]
#>
#> Test of heterogeneity:
#> Q d.f. p-value
#> 57.13 25 0.0003
#>
#> Details of meta-analysis methods:
#> - Inverse variance method
#> - Paule-Mandel estimator for tau^2
#> - Q-Profile method for confidence interval of tau^2 and tau
#> - Calculation of I^2 based on Q
#> - Trim-and-fill method to adjust for funnel plot asymmetry (L-estimator)
Note, the printout for the limit meta-analysis is not shown in this vignette as the installation of R package metasens is optional.
Create Figure 5 (file ‘figure5.pdf’).
pdf("figure5.pdf", width = 10, height = 10)
#
par(mfrow = c(2, 2), pty = "s",
oma = c(0, 0, 0, 0), mar = c(4.1, 3.1, 2.1, 1.1))
#
funnel(m.publ, xlim = c(0.05, 50), axes = FALSE)
axis(1, at = c(0.1, 0.2, 0.5, 1, 2, 5, 10, 50))
axis(2, at = c(0, 0.5, 1, 1.5))
box()
title(main = "Panel A: Funnel plot", adj = 0)
#
funnel(m.publ, xlim = c(0.05, 50), axes = FALSE,
contour.levels = c(0.9, 0.95, 0.99),
col.contour = c("darkgray", "gray", "lightgray"))
legend("topright",
c("p < 1%", "1% < p < 5%", "5% < p < 10%", "p > 10%"),
fill = c("lightgray", "gray", "darkgray", "white"),
border = "white", bg = "white")
axis(1, at = c(0.1, 0.2, 0.5, 1, 2, 5, 10, 50))
axis(2, at = c(0, 0.5, 1, 1.5))
box()
title(main = "Panel B: Contour-enhanced funnel plot", adj = 0)
#
funnel(tf.publ, xlim = c(0.05, 50), axes = FALSE)
axis(1, at = c(0.1, 0.2, 0.5, 1, 2, 5, 10, 50))
axis(2, at = c(0, 0.5, 1, 1.5))
box()
title(main = "Panel C: Trim-and-fill method", adj = 0)
#
funnel(l1.publ, xlim = c(0.05, 50), axes = FALSE,
col.line = 8, lwd.line = 3)
axis(1, at = c(0.1, 0.2, 0.5, 1, 2, 5, 10, 50))
axis(2, at = c(0, 0.5, 1, 1.5))
box()
title(main = "Panel D: Limit meta-analysis", adj = 0)
#
dev.off()