Mapping R workflows to PyMetaAnalysis¶
This guide helps users translate conventional R meta and metafor
workflows into PyMetaAnalysis calls. It is a terminology and configuration
map, not a claim that similarly named functions always use identical formulas
or defaults.
Always compare the resolved configuration and numerical output when porting an analysis. See validation for the R versions, calls, and fixtures used by this project.
Entry points¶
| Analysis | PyMetaAnalysis | R metafor |
R meta |
|---|---|---|---|
| Generic effects and variances | meta_analysis() |
rma.uni(yi, vi, ...) |
metagen(TE, seTE, ...) |
| Binary 2x2 tables, inverse variance | meta_binary(..., method="IV") |
escalc() then rma.uni() |
metabin(..., method="Inverse") |
| Binary 2x2 tables, Mantel-Haenszel | meta_binary(..., method="MH") |
rma.mh() |
metabin(..., method="MH") |
| Binary 2x2 tables, Peto OR | meta_binary(..., measure="OR", method="Peto") |
rma.peto() |
metabin(..., sm="OR", method="Peto") |
| Continuous group summaries | meta_continuous() |
escalc() then rma.uni() |
metacont() |
| Correlations and sample sizes | meta_correlation() |
escalc(measure="ZCOR") then rma.uni() |
metacor(sm="ZCOR") |
| Subgroups | subgroup= on a high-level call |
separate fits or a moderator model | subgroup= |
| Leave-one-out | result.leave_one_out() |
leave1out() for supported fits |
metainf() |
| Meta-regression influence | regression.influence() |
influence(), rstudent(), cooks.distance(), dfbetas() |
— |
| Meta-regression collinearity | regression.collinearity() |
vif() plus weighted design diagnostics |
— |
| Meta-regression linear contrasts | regression.contrast(...) |
anova(..., X=..., rhs=...) |
— |
| Cumulative analysis | result.cumulative() |
cumul() |
metacum() |
| Contour-enhanced funnel plot | result.funnel(contour_levels=(...)) |
funnel(level=c(...), refline=...) |
funnel(contour.levels=c(...), ref=...) |
| Trim-and-fill | result.trim_and_fill() |
trimfill() |
trimfill() |
| Begg-Mazumdar rank test | result.begg_test() |
ranktest() |
metabias(..., method.bias="Begg") (definition may differ) |
| Classical Egger test | result.egger_test() |
regtest(..., model="lm", predictor="sei") |
metabias(..., method.bias="Egger") |
| Harbord binary-OR test | result.harbord_test() |
manual documented score regression | metabias(..., method.bias="Harbord") |
| Peters binary-OR test | result.peters_test() |
manual documented WLS | metabias(..., method.bias="Peters") |
begg_test(exact=True) matches metafor::ranktest()'s exact default for
tie-free data. PyMetaAnalysis's exact=None automatic policy switches to the
asymptotic calculation at 50 studies to avoid an unexpectedly expensive exact
request; pass exact=True explicitly when that R-compatible behavior is
required. Its optional continuity correction is independently checked against
stats::cor.test(..., method="kendall", exact=FALSE, continuity=TRUE) on the
same standardized response.
trim_and_fill() follows metafor::trimfill() for L0/R0 rank estimators,
automatic side selection, source-model refitting, and mirrored variances. Its
result deliberately labels imputed rows as synthetic, exposes the selection
slope and iteration trace, and requires at least three included studies.
PyMetaAnalysis intentionally has no metabin, metacont, or rma aliases.
One documented Python entry point per input shape keeps result types and
provenance behavior consistent.
Input names¶
| Meaning | PyMetaAnalysis | R metafor |
R meta |
|---|---|---|---|
| Study effect | effect |
yi |
TE |
| Sampling variance | variance |
vi |
square of seTE |
| Study label | study or DataFrame index |
slab |
studlab |
| Treatment events | event_treat |
ai |
event.e |
| Treatment total | n_treat |
n1i |
n.e |
| Control events | event_control |
ci |
event.c |
| Control total | n_control |
n2i |
n.c |
| Treatment mean/SD | mean_treat, sd_treat |
m1i, sd1i |
mean.e, sd.e |
| Control mean/SD | mean_control, sd_control |
m2i, sd2i |
mean.c, sd.c |
| Correlation/sample size | correlation, n |
ri, ni |
cor, n |
PyMetaAnalysis accepts DataFrame column names or aligned one-dimensional
array-like values. When study= is omitted for a DataFrame, its index supplies
the display labels.
Measures and scales¶
PyMetaAnalysis measure |
Meaning | metafor measure |
meta sm |
Model scale |
|---|---|---|---|---|
"OR" |
Odds ratio | "OR" |
"OR" |
log ratio |
"RR" |
Risk ratio | "RR" |
"RR" |
log ratio |
"RD" |
Risk difference | "RD" |
"RD" |
identity |
"MD" |
Mean difference | "MD" |
"MD" |
identity |
"SMD" |
Exact-corrected Hedges' g | "SMD" with the documented correction |
"SMD" with exact Hedges correction |
identity |
"ZCOR" |
Fisher's z-transformed correlation | "ZCOR" |
"ZCOR" |
Fisher's z |
For OR and RR, estimate and ci remain on the log model scale;
display_estimate and display_ci provide exponentiated ratios. For ZCOR,
the model attributes remain Fisher's z values and the display attributes apply
tanh to return correlations. This is similar to choosing transformed or
untransformed printing in R, but both scales remain explicit attributes in
Python.
Models, pooling, and heterogeneity¶
| PyMetaAnalysis | metafor analogue |
meta analogue |
Notes |
|---|---|---|---|
model="common" |
rma.uni(..., method="EE") |
common=TRUE, random=FALSE |
Inverse-variance common-effect fit |
model="random" |
random-effects rma.uni() |
random=TRUE |
Requires a tau-squared policy |
method="IV" |
inverse-variance weighting | method="Inverse" |
Binary API only; generic, continuous, and correlation fits are IV |
method="MH" |
rma.mh() |
method="MH" |
Common-effect OR/RR/RD |
method="Peto" |
rma.peto() |
method="Peto" |
Common-effect OR; raw pooling tables and O-minus-E heterogeneity |
tau2_method=None (resolved as "REML") |
method="REML" |
method.tau="REML" |
PyMetaAnalysis random-effects default |
tau2_method="PM" |
method="PM" |
method.tau="PM" |
Paule-Mandel |
tau2_method="DL" |
method="DL" |
method.tau="DL" |
DerSimonian-Laird |
The same label does not guarantee identical optimizer tolerances, boundary
handling, or heterogeneity definitions. In particular, PyMetaAnalysis records
i2_method; random-effects inverse-variance results use the documented
tau-squared/typical-variance definition, while common-effect and MH results use
the Q-based definition.
Peto also reports Q-based inconsistency, but its Q is formed from the Peto
observed-minus-expected contributions rather than ordinary study-log-OR
inverse-variance residuals.
result.tau2_confidence_interval() corresponds to
confint(fit, type="QP") for an rma.uni random-effects fit. Both return
Q-profile bounds for tau-squared and monotonic tau, I-squared, and H-squared
transformations. PyMetaAnalysis additionally exposes is_empty so a formal
empty confidence set is distinguishable from its constrained [0, 0]
display.
Confidence and prediction intervals¶
PyMetaAnalysis ci_method |
metafor |
R meta |
Behavior |
|---|---|---|---|
"normal" |
default test="z" |
method.random.ci="classic" |
Normal mean interval |
"hartung_knapp" |
test="knha" |
method.random.ci="HK" |
Unmodified HK variance and t quantile |
"hartung_knapp_adhoc" |
test="adhoc" |
HK plus an explicitly selected ad hoc correction | HK variance cannot fall below the classic variance |
Eligible random-effects fits use the documented HTS prediction interval under
normal inference. Either Hartung-Knapp choice instead uses its selected
pooled-mean variance with k-2 degrees of freedom (HK-PR), matching
predict(fit, predtype="Riley") in metafor. R packages offer additional
prediction-interval choices, so method settings must still be compared
explicitly.
Meta-regression uses a separate prediction rule. Its default corresponds to
predict(fit) in metafor: normal inference uses a normal critical value and
Hartung-Knapp inference uses t_(k-p). PyMetaAnalysis
prediction_interval_method="riley" corresponds to
predict(fit, predtype="Riley"), using t_(k-p-1) for the true-effect
prediction interval while leaving the mean-effect confidence interval
unchanged.
Sparse binary studies¶
The closest names are:
| PyMetaAnalysis | R meta |
Meaning |
|---|---|---|
continuity_correction |
incr |
Increment used for corrected study effects |
correction_scope="only_zero_studies" |
method.incr="only0" |
Correct only studies containing a zero cell |
correction_scope="if_any_zero" |
method.incr="if0all" |
Correct every study when any study contains a zero cell |
correction_scope="all_studies" |
method.incr="all" |
Correct every study |
correction_scope="none" |
no increment | Disable study-effect correction |
mh_continuity_correction=None |
MH.exact=TRUE in intent |
Avoid correction for exact MH pooling where defined |
These are conceptual mappings, not interchangeable switches. R meta also
supports dataset-wide correction scopes and methods that PyMetaAnalysis does
not implement. For OR/RR, double-zero and double-all studies are excluded from
all model and heterogeneity calculations by default while remaining visible in
the result table. RD uses its separate rd_zero_variance policy.
For measure="RD", PyMetaAnalysis and metafor::rma.mh(measure="RD") use
the Sato-Greenland-Robins sampling variance. A separate explicit
mh_continuity_correction changes the MH tables; the study-effect
continuity_correction still affects only displayed study variances and
heterogeneity.
For Peto, PyMetaAnalysis's default study-level correction corresponds to
escalc(measure="PETO", add=0.5, to="only0", drop00=TRUE) for the displayed
study rows. Its pooled fit corresponds to rma.peto() with raw pooling tables;
the study correction never changes the pooled result. Compare Peto's
rare-outcome, within-study arm-balance, and modest-effect assumptions before
porting it solely because a table contains zeros.
Read zero-event studies before translating sparse analyses.
Worked generic translation¶
Python:
import pandas as pd
import meta_analyze as ma
studies = pd.DataFrame(
{"yi": [0.12, 0.35, -0.08, 0.21], "vi": [0.04, 0.06, 0.03, 0.05]}
)
studies["sei"] = studies["vi"] ** 0.5
result = ma.meta_analysis(
data=studies,
effect="yi",
variance="vi",
model="random",
tau2_method="REML",
ci_method="hartung_knapp_adhoc",
)
The corresponding metafor configuration is conceptually:
rma.uni(
yi = yi,
vi = vi,
method = "REML",
test = "adhoc",
data = studies
)
The corresponding R meta configuration starts from standard errors rather
than variances. PyMetaAnalysis can accept that uncertainty column directly as
standard_error=; no manual squaring step is required:
result = ma.meta_analysis(
studies,
effect="yi",
standard_error="sei",
model="random",
tau2_method="REML",
ci_method="hartung_knapp_adhoc",
)
metagen(
TE = yi,
seTE = sqrt(vi),
common = FALSE,
random = TRUE,
method.tau = "REML",
method.random.ci = "HK",
data = studies
)
Check the R package's explicit ad hoc HK option before treating the last call as numerically equivalent.
Classical Egger regression¶
egger = result.egger_test()
corresponds to the classical metafor configuration:
regtest(fit, model = "lm", predictor = "sei")
PyMetaAnalysis does not currently expose metafor's default
model="rma" version. The returned intercept is the tested asymmetry
coefficient, while limit_estimate is the extrapolated effect as the standard
error tends to zero. Review the dedicated
small-study-effects guide before treating similarly
named R functions as numerically interchangeable.
Harbord regression for binary odds ratios¶
binary_or_result = ma.meta_binary(
event_treat=[1, 2, 4, 3, 5],
n_treat=[80, 90, 100, 110, 120],
event_control=[3, 5, 2, 4, 7],
n_control=[85, 100, 110, 100, 125],
measure="OR",
)
harbord = binary_or_result.harbord_test()
corresponds to:
metabias(binary_or_fit, method.bias = "Harbord")
PyMetaAnalysis calculates the null efficient score Z and score variance V
directly from retained treatment/control counts. It fits the standardized form
Z/sqrt(V) on sqrt(V), which is algebraically equivalent to the
V-weighted Z/V on 1/sqrt(V) regression used by R meta. The intercept is
the tested asymmetry coefficient, with multiplicative dispersion and
t_(k-2) inference. Study-level continuity corrections and the source pooling
method do not enter the score calculation.
Peters regression for binary odds ratios¶
peters = binary_or_result.peters_test()
corresponds to:
metabias(binary_or_fit, method.bias = "Peters")
PyMetaAnalysis requires an OR result created by meta_binary() because Peters
regression needs the original treatment/control counts. It reconstructs
conventional continuity-corrected study log odds ratios, uses inverse total
sample size as predictor and S*F/N weights, and applies multiplicative
dispersion with t_(k-2) inference. slope is the tested coefficient;
limit_estimate is the extrapolated log OR at infinite total sample size.
The source common/random and MH/IV/Peto pooling choice does not enter this
separate regression.