The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Responder analysis from a fitted meta-analysis

You often already have a continuous meta-analysis fitted and saved in an R object. respondeR can read that object directly and express it as responder risks, so you do not have to rebuild the arm-level data frame by hand. It reads the saved object with base R, so the package that produced it (for example meta or metafor) does not even need to be attached.

There are two situations, handled automatically:

From a meta (metacont) object

A metacont object stores both arms, so respondeR can run the complete analysis. You would normally create it like this:

library(meta)
fit <- metacont(
  n.e = n_e, mean.e = change_e, sd.e = sd_e,
  n.c = n_c, mean.c = change_c, sd.c = sd_c,
  studlab = study, data = sample_responder_data
)
responder_analysis(fit, mid = 1)

A metacont object is, in the end, a list with the per-arm fields respondeR reads (mean.e, sd.e, n.e, mean.c, sd.c, n.c, studlab). To keep this vignette runnable without meta installed, here is the same object built by hand:

d <- sample_responder_data
fit_meta <- structure(
  list(
    studlab = d$study,
    n.e = d$n_e, mean.e = d$change_e, sd.e = d$sd_e,
    n.c = d$n_c, mean.c = d$change_c, sd.c = d$sd_c,
    sm = "MD"
  ),
  class = c("metacont", "meta")
)

responder_analysis(fit_meta, mid = 1)[, c("method", "p_e", "p_c", "rd", "rr", "or")]
#>       method       p_e       p_c        rd       rr       or
#> 1 individual        NA        NA 0.2548985 2.148360 3.197922
#> 2   weighted 0.4757294 0.2205372 0.2551921 2.157138 3.207140
#> 3 unweighted 0.4767051 0.2279613 0.2487438 2.091167 3.085185
#> 4     median 0.4869694 0.2150781 0.2718912 2.264151 3.464085

That is identical to calling responder_analysis() on the data frame; all the usual arguments (direction, method, pooling, control, …) pass straight through. The fitted object’s own choices carry over unless you override them: studies excluded from the fit (its exclude argument) are dropped, a random-effects fit (common = FALSE, random = TRUE) sets pooling = "random", its method.tau (DL or REML) sets tau_method, and a Hartung-Knapp fit sets ci_method = "hksj". Repeated study labels (the comparisons of a multi-arm trial) become a trial column, so a shared control arm is split rather than double counted. If you only want the extracted data frame, use as_responder_data().

From a metafor rma object

An rma fit keeps the pooled relative effect, not the arms. respondeR reads the estimate, its standard error, its confidence interval (so a Knapp-Hartung t interval and the fit’s confidence level are kept) and the effect measure, then converts. Because the arms are gone, you must tell it the baseline control responder risk (as a probability, or as a control mean and SD), by name. Meta-regressions, models without an intercept and raw-mean measures are rejected.

library(metafor)
#> Warning: package 'metafor' was built under R version 4.6.1
#> Loading required package: Matrix
#> Warning: package 'Matrix' was built under R version 4.6.1
#> Loading required package: metadat
#> Loading required package: numDeriv
#> 
#> Loading the 'metafor' package (version 5.2-1). For an
#> introduction to the package please type: help(metafor)
es <- escalc(
  measure = "SMD", m1i = change_e, sd1i = sd_e, n1i = n_e,
  m2i = change_c, sd2i = sd_c, n2i = n_c, data = d
)
fit_rma <- rma(yi, vi, data = es)

# A standardized mean difference, against a 22% control responder rate:
responder_analysis(fit_rma, baseline = 0.22)[, c("method", "p_e", "p_c", "rd", "or")]
#>    method       p_e  p_c        rd       or
#> 1 metafor 0.4994262 0.22 0.2794262 3.537327

The effect is read as experimental minus control and oriented by direction. For pain, where a reduction is the response, a negative SMD is a benefit:

es_vas <- escalc(
  measure = "SMD", m1i = change_e, sd1i = sd_e, n1i = n_e,
  m2i = change_c, sd2i = sd_c, n2i = n_c, data = vas_pain
)
fit_vas <- rma(yi, vi, data = es_vas, test = "knha")
responder_analysis(fit_vas, baseline = 0.4, direction = "lower")
#> Responder analysis (respondeR)
#>   Percentages; 95% confidence intervals.
#> 
#>   Method   PE   PC                  RD                  RR                  OR
#>  metafor 76.3 40.0 36.3 (27.3 to 43.5) 1.91 (1.68 to 2.09) 4.84 (3.09 to 7.57)
#>                    NNT
#>  NNTB 2.8 (2.3 to 3.7)
#> 
#> Use as.data.frame() for every column, summary() for details, plot() for a forest plot.

For a mean-difference model you also pass a reference within-arm SD (sd_ref), the scale on which the threshold lives, and you can give the baseline as a control mean and SD:

es_md <- escalc(
  measure = "MD", m1i = change_e, sd1i = sd_e, n1i = n_e,
  m2i = change_c, sd2i = sd_c, n2i = n_c, data = d
)
fit_md <- rma(yi, vi, data = es_md)

responder_analysis(fit_md, baseline = list(mean = 0, sd = 1.3),
                   mid = 1, sd_ref = 1.3)[, c("method", "p_e", "p_c", "rd")]
#>    method       p_e       p_c        rd
#> 1 metafor 0.4696273 0.2208782 0.2487491

From a bare estimate

If all you have is a number from a paper (a pooled SMD or MD with its SE), call the engine directly:

# Pooled SMD of 0.5 (SE 0.1) vs a 25% control responder rate:
responder_from_effect(0.5, se = 0.1, measure = "SMD", baseline = 0.25)[,
  c("method", "p_e", "p_c", "rd", "or")]
#>   method       p_e  p_c        rd       or
#> 1    SMD 0.4522181 0.25 0.2022181 2.476632

How the conversion works, and what it assumes

For a standardized mean difference g, respondeR maps it to an odds ratio through the logistic distribution, lnOR = (pi / sqrt(3)) g (Hasselblad and Hedges, 1995; Chinn, 2000; Anzures-Cabrera, Sarpatwari & Higgins, 2011), then combines that odds ratio with the control responder proportion p_c to recover the experimental proportion and the risk difference, risk ratio, odds ratio and NNT.

For a mean difference MD, respondeR shifts the control responder probability along the Normal cut-point: p_e = Phi(Phi^-1(p_c) + MD / sigma), where sigma is the reference within-arm SD (sd_ref). This conversion assumes Normal change scores. Both risks depend on the same baseline, so when the baseline is estimated (a mean, SD and n) its uncertainty is propagated jointly with that of the effect.

Two assumptions matter. First, the baseline is yours to choose: a defensible control responder rate, taken from a representative control arm or an external source, much as a GRADE summary-of-findings table fixes one baseline risk. A fixed baseline probability gives intervals conditional on it. Second, the mean-difference path needs a reference SD; pick the typical within-arm SD on the outcome scale, and report it.

Coming next

Support for network meta-analysis objects (netmeta) and Bayesian network meta-analysis / multilevel network meta-regression (multinma) is planned for a later release: each comparison or posterior draw will be converted to responder risks against a chosen baseline.

References

Thorlund, K., Walter, S. D., Johnston, B. C., Furukawa, T. A., & Guyatt, G. H. (2011). Pooling health-related quality of life outcomes in meta-analysis. Research Synthesis Methods, 2(3), 188 to 203. doi:10.1002/jrsm.46

Anzures-Cabrera, J., Sarpatwari, A., & Higgins, J. P. T. (2011). Expressing findings from meta-analyses of continuous outcomes in terms of risks. Statistics in Medicine, 30(25), 2967 to 2985. doi:10.1002/sim.4298

Chinn, S. (2000). A simple method for converting an odds ratio to effect size for use in meta-analysis. Statistics in Medicine, 19(22), 3127 to 3131.

Hasselblad, V., & Hedges, L. V. (1995). Meta-analysis of screening and diagnostic tests. Psychological Bulletin, 117(1), 167 to 178.

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.