Effect sizes¶
Most analyses start from raw study data (2x2 tables, means and standard deviations, correlations, proportions) that
escalc converts to an effect size yi and its sampling variance vi. If the studies already report effect sizes and
standard errors, skip escalc and pass them to rma directly.
escalc¶
escalc takes a measure and the arguments that measure needs, and returns a pandas DataFrame. With data= the new
columns yi and vi are appended to your data.
from metafopy import datasets, escalc
dat = datasets.load('dat.curtis1998')
dat = escalc(measure='ROM', m1i='m1i', sd1i='sd1i', n1i='n1i', m2i='m2i', sd2i='sd2i', n2i='n2i', data=dat)
print(dat[['id', 'genus', 'species', 'yi', 'vi']].head())
id genus species yi vi
0 21 ALNUS RUBRA 0.550192 0.038472
1 22 ALNUS RUBRA 0.147040 0.017455
2 27 ACER RUBRUM 0.437769 0.032753
3 32 QUERCUS PRINUS -0.110832 0.029516
4 35 MALUS DOMESTICA 0.117135 0.046786
Here ROM is the log ratio of means, comparing woody plant mass under elevated and ambient CO2, the example of Hedges et
al. (1999), https://doi.org/10.1890/0012-9658(1999)080[1150:TMAORR]2.0.CO;2. A ratio of means suits outcomes that are
positive and measured on a ratio scale.
The measures are metafor's, with the same abbreviations, and fall into groups by the data they need:
| Data | Arguments | Measures, for example |
|---|---|---|
| 2x2 tables | ai, bi, ci, di (or n1i, n2i with ai, ci) |
RR, OR, RD, PETO, AS, PHI, YUQ, RTET |
| Person-time | x1i, x2i, t1i, t2i |
IRR, IRD, IRSD |
| Two group means | m1i, m2i, sd1i, sd2i, n1i, n2i |
MD, SMD, SMDH, ROM, VR, CVR |
| Correlations | ri, ni |
COR, ZCOR |
| Proportions | xi, ni |
PR, PLO, PAS, PFT |
| Incidence rates | xi, ti |
IR, IRLN, IRS, IRFT |
| Single means | mi, sdi, ni |
MN, MNLN, SMN, SDLN, CVLN |
| Change scores | m1i, m2i, sd1i, sd2i, ni, ri |
MC, SMCC, SMCR, ROMC |
| Partial and semi-partial correlations, R^2 | ri, ti, ni, mi, r2i |
PCOR, SPCOR, R2, ZR2 |
| Precomputed | yi, vi or sei |
GEN |
metafopy.effects.measures.MEASURES is the full list, and the escalc reference the
required arguments of each. The metafor documentation describes the formulas:
https://wviechtb.github.io/metafor/reference/escalc.html.
Zero cells and bias correction¶
Counts of zero make log ratios undefined. add, to and drop00 control the continuity correction exactly as in
metafor: by default 0.5 is added to all four cells of a study that has a zero cell (to='only0'). Set add=0 to turn
this off, or to='all' to correct every study.
from metafopy import escalc
zero = datasets.load('dat.hine1989')
print(escalc(measure='OR', ai='ai', n1i='n1i', ci='ci', n2i='n2i', data=zero)[['yi', 'vi']])
print(escalc(measure='OR', ai='ai', n1i='n1i', ci='ci', n2i='n2i', data=zero, add=0)[['yi', 'vi']])
yi vi
0 0.819899 1.550837
1 0.000000 0.550000
2 0.453784 0.436002
3 0.326001 0.363800
4 0.847298 0.495608
5 1.004583 0.354944
yi vi
0 0.819899 1.550837
1 0.000000 0.550000
2 0.453784 0.436002
3 0.326001 0.363800
4 0.847298 0.495608
5 1.004583 0.354944
Measures such as SMD apply the small-sample bias correction of Hedges (1981), https://doi.org/10.3102/10769986006002107, by default (correct=True). For the choice
of sampling variance formula see vtype, described in the metafor documentation.
Correlations and proportions¶
The raw correlation has a variance that depends on the correlation itself. Meta-analyses of correlations therefore work on
Fisher's z (ZCOR) and back-transform for presentation, following Borenstein et al. (2009),
https://doi.org/10.1002/9780470743386:
import numpy
from metafopy import predict, rma
mol = datasets.load('dat.molloy2014')
mol = escalc(measure='ZCOR', ri='ri', ni='ni', data=mol)
res = rma('yi', 'vi', data=mol)
print(predict(res, transf=numpy.tanh))
pred ci.lb ci.ub pi.lb pi.ub
0 0.1488 0.0878 0.2087 -0.0371 0.3248
For proportions, the logit (PLO) is a common choice, with transf.ilogit to go back:
from metafopy import transf
pri = datasets.load('dat.pritz1997')
pri = escalc(measure='PLO', xi='xi', ni='ni', data=pri)
print(predict(rma('yi', 'vi', data=pri), transf=transf.ilogit))
pred ci.lb ci.ub pi.lb pi.ub
0 0.7575 0.6605 0.8337 0.4661 0.9179
Precomputed effect sizes¶
If a dataset already has effect sizes and sampling variances, pass them straight to rma, or to escalc with
measure='GEN' to attach labels. Standard errors work too, with sei=:
from metafopy import rma
rau = datasets.load('dat.raudenbush1985')
print(rma('yi', 'vi', data=rau))
Random-Effects Model (k = 19; tau^2 estimator: REML)
tau^2 (estimated amount of total heterogeneity): 0.0188 (SE = 0.0155)
tau (square root of estimated tau^2 value): 0.1372
I^2 (total heterogeneity / total variability): 41.86%
H^2 (total variability / sampling variability): 1.72
Test for Heterogeneity:
Q(df = 18) = 35.8295, p-val = 0.0074
Model Results:
estimate se zval pval ci.lb ci.ub
0.0837 0.0516 1.6208 0.1051 -0.0175 0.1849
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Study labels, subsets and flipping¶
slab gives study labels used in plots and printed output, subset selects studies, and flip changes the sign of the
effect size of selected studies. All three accept a column name or an array.
stay = escalc(measure='MD', n1i='n1i', n2i='n2i', m1i='m1i', m2i='m2i', sd1i='sd1i', sd2i='sd2i', data=datasets.load('dat.normand1999'), slab='source')
print(stay.attrs['slab'])
['Edinburgh', 'Orpington-Mild', 'Orpington-Moderate', 'Orpington-Severe', 'Montreal-Home', 'Montreal-Transfer', 'Newcastle', 'Umea', 'Uppsala']
Summarising¶
summary_escalc adds the standard errors, z values, p values and confidence limits of each study to an escalc result:
from metafopy import summary_escalc
print(summary_escalc(stay)[['source', 'yi', 'sei', 'zi', 'pval', 'ci.lb', 'ci.ub']])
source yi sei ... pval ci.lb ci.ub
0 Edinburgh -20.0 6.364591 ... 1.675772e-03 -32.474370 -7.525630
1 Orpington-Mild -2.0 1.442444 ... 1.655835e-01 -4.827139 0.827139
2 Orpington-Moderate -55.0 3.962121 ... 8.206280e-44 -62.765615 -47.234385
3 Orpington-Severe -71.0 12.256518 ... 6.920755e-09 -95.022333 -46.977667
4 Montreal-Home -4.0 4.160251 ... 3.363107e-01 -12.153943 4.153943
5 Montreal-Transfer 1.0 1.080436 ... 3.546786e-01 -1.117615 3.117615
6 Newcastle 11.0 9.725694 ... 2.580447e-01 -8.062010 30.062010
7 Umea -10.0 2.512146 ... 6.872424e-05 -14.923716 -5.076284
8 Uppsala 7.0 4.454471 ... 1.160770e-01 -1.730602 15.730602
[9 rows x 7 columns]
Transformations¶
metafopy.transf holds metafor's transformation functions (transf.ilogit, transf.ztor, transf.dtocles and so on, also
imported at the top level as transf_ilogit, transf_ztor, ...). Pass
them as transf= to predict and confint, or as atransf= to the plot functions. NumPy functions such as numpy.exp and
numpy.tanh work as well.