Statistical methods¶
This page specifies the calculations implemented by PyMetaAnalysis. It is an implementation reference, not guidance for choosing an estimand or replacing a review protocol.
Notation¶
For study i:
y_iis the study effect on the model scale;v_iis its sampling variance;w_iis an analysis weight;kis the number of included studies;tau^2is the estimated between-study variance.
Treatment and control are ordered exactly as their argument names imply. A positive difference measure means treatment minus control is positive.
Inverse-variance pooling¶
Common-effect weights and the pooled estimate are:
w_i = 1 / v_i
y_hat = sum(w_i y_i) / sum(w_i)
Var(y_hat) = 1 / sum(w_i)
Random-effects weights replace v_i with v_i + tau^2:
w_i* = 1 / (v_i + tau^2)
mu_hat = sum(w_i* y_i) / sum(w_i*)
Var(mu_hat) = 1 / sum(w_i*)
Reported normalized_weight values equal the selected raw weights divided by
their sum. Pooled estimates, Q statistics, and tau-squared estimating
equations use relative weights internally when necessary to avoid overflow
without changing their ratios or equation roots. A sampling variance too
small to produce a finite float64 inverse weight is rejected explicitly.
Between-study variance¶
Random-effects inverse-variance models require at least two included studies. The default estimator is REML.
DerSimonian-Laird¶
With common-effect weights, Cochran's Q, df = k - 1, and:
C = sum(w_i) - sum(w_i^2) / sum(w_i)
tau^2_DL = max(0, (Q - df) / C)
DL is closed form and records zero iterations.
Internally, C is evaluated as 2 * sum(i < j, w_i * w_j) / sum(w_i)
using relative weights and cumulative sums. This avoids subtracting nearly
equal totals when one study dominates. Residual calculations first subtract
the highest-weight study's effect, then center those offsets. The REML score
also divides by the positive weight trace before subtraction. These are
algebraic rearrangements, not a rule that forces extreme inputs to zero
heterogeneity. Unrepresentable relative precisions raise
InvalidStudyDataError; an unrepresentable iterative bracket raises
ConvergenceError.
Paule-Mandel¶
PM solves:
Q(tau^2) = sum((y_i - mu_hat(tau^2))^2 / (v_i + tau^2)) = k - 1
If the equation is already non-positive at zero, the estimate is the zero boundary. Otherwise PyMetaAnalysis brackets the non-negative root and solves it with a bounded scalar root finder.
The numerical root equation is multiplied by a positive variance scale before evaluation. This leaves its root unchanged while avoiding overflow from raw inverse weights.
Restricted maximum likelihood¶
REML solves the restricted-likelihood score equation over non-negative
tau^2. As with PM, a non-positive score at zero produces a boundary estimate;
otherwise a bracketed root is found iteratively.
The implemented score is evaluated in an algebraically equivalent scaled form; the positive scale factor does not change its sign or root.
result.diagnostics records convergence, iteration count, and whether the
solution reached the zero boundary. Boundary means the constrained estimate is
exactly zero; a positive numerical root is not relabeled as a boundary merely
because it is smaller than the configured absolute root-finding tolerance.
Failure to bracket or converge raises ConvergenceError; no fallback estimator
is selected silently.
Confidence intervals¶
Normal interval¶
The default interval is:
estimate +/- z_(1 - alpha/2) * SE
This is available for common- and random-effects inverse-variance models and for common-effect Mantel-Haenszel and Peto pooling.
Hartung-Knapp intervals¶
For random-effects inverse-variance models:
q_star = sum(w_i* (y_i - mu_hat)^2) / (k - 1)
Var_HK(mu_hat) = q_star / sum(w_i*)
CI = mu_hat +/- t_(k - 1, 1 - alpha/2) * sqrt(Var_HK)
hartung_knapp uses this variance without modification. If it is below the
classic variance, the result records a warning. hartung_knapp_adhoc instead
uses:
max(Var_classic, Var_HK)
The variants are separate public choices. PyMetaAnalysis does not silently apply the safeguard to an unmodified Hartung-Knapp request.
Prediction interval¶
Random-effects inverse-variance models with at least three included studies
use a k - 2 prediction rule:
mu_hat +/- t_(k - 2, 1 - alpha/2)
* sqrt(tau^2 + Var_selected(mu_hat))
For ci_method="normal", Var_selected is the classic pooled-mean variance
and the method is recorded as HTS. For either Hartung-Knapp choice, it is the
same unmodified or safeguarded HK variance used by the mean interval, and the
prediction method is recorded as HK-PR. This matches
metafor::predict(..., predtype="Riley"). No interval is returned below three
studies. An interval calculated with three or four studies carries an explicit
uncertainty warning.
Heterogeneity and inconsistency¶
For inverse-variance fits, Cochran's Q uses common-effect inverse-variance weights:
Q = sum(w_i (y_i - y_hat_common)^2)
df = k - 1
p = P(chi-square_df >= Q)
For Mantel-Haenszel analyses, residuals are centered on the MH pooled estimate while the individual-effect inverse variances supply the Q weights. Peto uses the separate observed-minus-expected definition documented under Peto pooling.
Common-effect IV, MH, and Peto results use Q-based inconsistency:
I^2 = max(0, (Q - df) / Q)
H^2 = Q / df
This Q-based H^2 is untruncated and can be below one when Q < df, while
I^2 is truncated at zero (also when Q is zero). Consequently,
H^2 = 1 / (1 - I^2) does not hold after that truncation. This existing
convention differs from the tau-squared-based definition below.
Random-effects results use the fitted tau^2 and a typical within-study
variance:
C = sum(w_i) - sum(w_i^2) / sum(w_i)
v_typical = (k - 1) / C
I^2 = tau^2 / (tau^2 + v_typical)
H^2 = 1 + tau^2 / v_typical
Q-profile confidence interval¶
For a candidate non-negative value t, define generalized Q using weights
w_i(t) = 1 / (v_i + t):
mu_hat(t) = sum(w_i(t) y_i) / sum(w_i(t))
Q(t) = sum(w_i(t) (y_i - mu_hat(t))^2)
Under the conventional normal random-effects model with known sampling
variances, Q(tau^2) follows a chi-squared distribution with k-1 degrees of
freedom. For confidence level 1-alpha, PyMetaAnalysis numerically solves:
Q(tau2_low) = chi2_(k-1, 1-alpha/2)
Q(tau2_high) = chi2_(k-1, alpha/2)
and constrains both bounds to the non-negative parameter space. If the lower
root is negative but the upper root is non-negative, the reported lower bound
is zero. If both roots are negative, the formal confidence set is empty;
Tau2ConfidenceInterval records is_empty=True and uses [0, 0] as the
explicit constrained display, following metafor's output convention.
The tau, I-squared, and H-squared bounds are monotonic transformations of the same tau-squared bounds. The interval is exact only under its model assumptions; in applications with estimated sampling variances or non-normal effects it should be interpreted as an approximation. See Viechtbauer (2007).
result.i2_method records q_based or tau2_typical_variance. I-squared is a
proportion internally and is formatted as a percentage in human-readable
output. With one study, Q is zero and its p-value, I-squared, and H-squared are
unavailable.
Begg-Mazumdar rank-correlation test¶
For included model-scale effects y_i and sampling variances v_i, first
compute the common-effect center and standardized residual used by
metafor::ranktest():
w_i = 1 / v_i
theta = sum(w_i * y_i) / sum(w_i)
v_bar = 1 / sum(w_i)
y_i* = (y_i - theta) / sqrt(v_i - v_bar)
Then define Kendall's concordance statistic between y_i* and v_i:
S = sum_(i<j) sign((y_i*-y_j*) * (v_i-v_j))
tau_b = S / sqrt((n0-n1) * (n0-n2))
n0 = k * (k-1) / 2
Here n1 and n2 are the numbers of tied pairs in standardized responses and
variances.
Without ties and for k < 50, the default two-sided p-value uses the exact
permutation distribution of S. Otherwise the test uses the standard
tie-adjusted null variance of S and a normal approximation. An optional
continuity correction moves nonzero S one unit toward zero before
standardization.
The implementation computes tau-b with an O(k log k) rank algorithm and
calculates tie moments without pairwise matrices. The result is independent of
the fitted pooling model and matches metafor::ranktest(). It requires at
least three studies and variation in both effects and variances. See
small-study effects for inference controls
and interpretation limits.
Classical Egger regression test¶
For included study effect y_i, sampling variance v_i, and standard error
s_i = sqrt(v_i), the classical Egger regression is:
y_i / s_i = alpha + beta * (1 / s_i) + error_i
This is fitted as the algebraically equivalent weighted regression:
y_i = beta + alpha * s_i + epsilon_i
weight_i = 1 / v_i
Let X contain an intercept and s_i, let W = diag(1/v_i), and let
df = k-2:
b = (X' W X)^(-1) X' W y
phi = sum(w_i * (y_i - X_i b)^2) / df
Cov(b) = phi * (X' W X)^(-1)
The implementation uses a scaled singular-value decomposition rather than an
explicit inverse. The second coefficient is alpha, reported as the
EggerTestResult.intercept, and is tested against zero with a two-sided t test
using k-2 degrees of freedom. The first coefficient is beta, reported as
the model-scale limit_estimate; its interval uses the same t distribution.
This is the weighted-regression-with-multiplicative-dispersion version produced
by metafor::regtest(model="lm", predictor="sei"). It is distinct from an
additive random/mixed-effects meta-regression. The test requires at least three
studies and identifiable variation in standard errors. Interpretation and
effect-measure limitations are documented under
small-study effects.
Harbord efficient-score test for binary odds ratios¶
For an included two-group binary study, let a_i be treatment events,
n_ti and n_ci the group sizes, N_i=n_ti+n_ci, S_i the total events,
and F_i=N_i-S_i. The null efficient score and score variance are:
Z_i = a_i - S_i * n_ti / N_i
V_i = n_ti * n_ci * S_i * F_i / (N_i^2 * (N_i - 1))
Harbord regression is fitted in the standardized form:
Z_i / sqrt(V_i) = alpha + beta * sqrt(V_i) + epsilon_i
phi = sum(epsilon_i^2) / (k - 2)
b = [alpha, beta]'
Cov(b) = phi * (X' X)^(-1)
This is algebraically equivalent to fitting Z_i/V_i on 1/sqrt(V_i) with
weights V_i. alpha, reported as HarbordTestResult.intercept, is tested
against zero with a two-sided t_(k-2) test. beta is reported as the
efficient-score limit_estimate and is also available with an exponential
display transformation; it is not an automatic replacement for the source
pooled estimate.
The implementation calculates V_i through bounded group fractions and raw
event margins, then uses a column-scaled SVD rather than an explicit inverse.
It does not apply or depend on study-level continuity corrections, source
study-effect estimators, or source pooling weights. This matches
meta::metabias(method.bias="Harbord") for two-group odds ratios.
Applicability and interpretation limits are documented under
small-study effects.
Peters regression test for binary odds ratios¶
For each included two-group binary study, let y_i be its conventional log
odds ratio, N_i its raw total sample size, S_i its raw total event count,
and F_i=N_i-S_i its raw total non-event count. Peters regression fits:
y_i = beta_0 + beta_1 * (1 / N_i) + epsilon_i
w_i = S_i * F_i / N_i
df = k - 2
With X_i = [1, 1/N_i] and W=diag(w_i), the coefficients and multiplicative
dispersion covariance are:
b = (X' W X)^(-1) X' W y
phi = sum(w_i * (y_i - X_i b)^2) / df
Cov(b) = phi * (X' W X)^(-1)
The implementation uses scaled square-root weights and a column-scaled SVD;
it does not explicitly invert the information matrix. The tested asymmetry
coefficient is beta_1, reported as PetersTestResult.slope and compared with
zero using a two-sided t_(k-2) test. beta_0 is the log-OR limit estimate as
total sample size tends to infinity and is also exposed on the OR display
scale.
Study log odds ratios are reconstructed from retained raw four-cell counts
using the source result's recorded study-level continuity correction. The
S*F/N weights and 1/N predictor always use uncorrected raw margins. This
matches meta::metabias(method.bias="Peters") for two-group odds ratios and
does not depend on the source pooling model. Applicability and interpretation
limits are documented under
small-study effects.
Duval-Tweedie trim-and-fill¶
For a chosen missing side, effects are oriented so the algorithm trims the upper tail, sorted, and centered on the current inverse-variance pooled fit. Absolute centered effects receive first-occurrence ranks with their original signs. L0 uses the positive signed-rank sum; R0 uses the most extreme negative signed rank. The non-negative integer missing count is iterated until stable, then the selected extreme effects are mirrored around the final center while retaining their sampling variances. Observed and imputed effects are refitted with the source common/random model and tau-squared estimator.
With side=None, the sign of the sampling-standard-error coefficient from a
source-model-compatible meta-regression selects the side, matching
metafor::trimfill(). R0 reports its conventional test of k0=0; L0 does not
manufacture a p-value. This is an exploratory sensitivity method whose symmetry
assumption can fail under genuine heterogeneity or design differences.
First-occurrence ranks are assigned after a stable sort of the oriented
effects. Tied inputs have dedicated R comparisons, but those cases do not
establish equivalence for every possible tie pattern. If all included effects
are identical, no studies are imputed and missing-count uncertainty is
unavailable; this is an explicit boundary policy, whereas the pinned
metafor R0 implementation errors on that input. A negative L0 variance
approximation likewise yields an unavailable standard error, not zero.
Random-effects intermediate refits must retain at least two studies;
otherwise the operation raises ConvergenceError without an adjusted fit.
Meta-regression¶
For k study effects, let X be the full-rank k-by-p design matrix. It
contains an intercept by default plus numeric moderator columns and treatment-
coded categorical terms:
y = X beta + u + epsilon
epsilon ~ N(0, V), V = diag(v_i)
u ~ N(0, tau^2 I)
Given residual tau-squared:
W = diag(1 / (v_i + tau^2))
B = (X' W X)^(-1)
beta_hat = B X' W y
P = W - W X B X' W
residual df = k - p
The implementation power-of-two scales design columns, uses a weighted QR
solve, and returns coefficients and covariance in the supplied moderator
units. This makes rank checks and fitted results invariant to representable
positive changes of units without silently changing the user-visible design.
For a design containing a constant column, residual calculations first anchor
effects on a canonical observed value so a large common location does not erase
residual geometry. A rank-deficient design or k <= p is rejected; terms are
not silently removed and a pseudo-inverse is not used.
Residual tau-squared¶
For generalized DL, define W0 and P0 at tau-squared zero:
QE0 = y' P0 y
C = trace(P0)
tau^2_DL = max(0, (QE0 - (k-p)) / C)
PM solves:
y' P(tau^2) y = k - p
REML solves the restricted score:
y' P(tau^2)^2 y - trace(P(tau^2)) = 0
All estimators are constrained to non-negative tau-squared. PM and REML use a
bracketed scalar root; a non-positive equation at zero returns the boundary.
Residual tau-squared requires k-p > 0. A positive root remains an interior
solution regardless of its size relative to the numerical tolerance. Failure
raises ConvergenceError without estimator fallback.
Generalized DL and REML need trace(P(t)), which can be much smaller than the
two matrix traces in its usual subtractive expression. Internally this is
evaluated as the positive sum sum(w_i * (1-h_i)); high-leverage complements
use determinant ratios so precision imbalances do not cancel the residual
information. The REML equation is divided by this positive trace before its
final subtraction. These are algebraic rescalings and do not change an
estimating-equation root.
Coefficient and moderator inference¶
Normal inference uses covariance B, z coefficient tests, and a chi-squared
Wald test for one or more moderator terms.
For both Hartung-Knapp choices:
q = y' P(tau^2) y / (k-p)
Cov_HK(beta_hat) = q B
Coefficient tests use a t distribution with k-p degrees of freedom. Joint
tests divide their Wald statistic by the number of tested terms and use an F
distribution with that numerator df and k-p denominator df.
hartung_knapp_adhoc replaces q with max(1, q); unmodified
hartung_knapp retains q and warns when it is below one.
With exactly zero residual dispersion, unmodified HK covariance is zero and
coefficient intervals collapse to points. Scalar tests report an undefined
statistic for 0 / 0 and signed infinity for a nonzero coefficient divided by
zero. Global, moderator, and joint contrast tests are unavailable because
their covariance cannot be inverted. The result records a warning and reports
serialize unavailable statistics as JSON null. No automatic safeguard is
applied; hartung_knapp_adhoc remains a separate explicit choice.
The global moderator test covers every non-intercept term.
test_moderator(name) covers every encoded term for one original moderator,
so a multi-level category receives a joint test.
Residual heterogeneity and pseudo-R-squared¶
Residual heterogeneity uses:
QE = y' P0 y
QE ~ chi-square_(k-p) under the null
Common models derive residual I-squared and H-squared from QE. Mixed models use:
v_typical = (k-p) / trace(P0)
I^2_residual = tau^2 / (tau^2 + v_typical)
H^2_residual = 1 + tau^2 / v_typical
For mixed models with an intercept, the same rows are refitted with an intercept-only design and the same tau-squared estimator:
R^2_raw = 1 - tau^2_model / tau^2_null
R^2 = max(0, R^2_raw)
The raw value is retained. Pseudo-R-squared is unavailable when the null tau-squared is zero and is not equivalent to ordinary regression R-squared.
Meta-regression prediction¶
For a new design vector x0:
fitted = x0' beta_hat
Var_mean = x0' Cov(beta_hat) x0
Mean-effect intervals use the selected normal or t_(k-p) critical value. A
mixed model additionally reports a true-effect prediction interval:
fitted +/- critical * sqrt(tau^2 + Var_mean)
The default critical value is standard normal for normal inference and
t_(k-p) for either Hartung-Knapp mode. The optional Riley rule uses
t_(k-p-1) and therefore requires k-p >= 2. Both are approximations because
tau-squared is estimated; the Riley option is not selected automatically.
Neither interval includes a sampling variance for a future observed effect.
See ADR 0003 for the
compatibility and validation decision.
Meta-regression deletion diagnostics¶
For every included study i, influence() deletes that study and refits the
model with the same configuration, including a fresh residual tau-squared
estimate. Let beta_(-i), tau^2_(-i), and q_(-i) denote the deleted-model
coefficient estimate, residual variance estimate, and inference scale. The
deleted prediction and externally standardized residual are:
pred_(-i) = x_i' beta_(-i)
e_(-i) = y_i - pred_(-i)
SE(e_(-i)) = sqrt(
q_(-i) * (v_i + tau^2_(-i))
+ x_i' Cov(beta_(-i)) x_i
)
rstudent_i = e_(-i) / SE(e_(-i))
The inference scale is one for normal inference, the deleted-model Hartung-Knapp scale for unmodified Hartung-Knapp, and its lower-bounded value for the safeguarded variant.
Let delta_i = beta_hat - beta_(-i). Cook's distance is:
Cook_i = delta_i' Cov(beta_hat)^(-1) delta_i
For DFBETAS, the deleted tau-squared is inserted into the full design matrix:
B_i = (X' W(tau^2_(-i)) X)^(-1)
DFBETAS_ij = delta_ij / sqrt(q_(-i) * B_i[j,j])
This matches the deletion definitions used by R metafor for the supported
independent-effect models. The implementation uses linear solves and does not
substitute a pseudo-inverse.
The output provides three transparent screening references:
potential outlier: abs(rstudent_i) > z(0.975)
Cook's-distance flag: Cook_i > median(chi-square_p)
DFBETAS flag: any abs(DFBETAS_ij) > 1
They are heuristic review aids. The residual reference is pointwise rather than multiplicity-adjusted, and no flag automatically removes a study.
Meta-regression collinearity diagnostics¶
VIF/GVIF uses the correlation matrix R of the classic coefficient covariance
for non-intercept terms. A scalar Hartung-Knapp adjustment does not change this
correlation matrix. For a term set S with m encoded coefficients:
GVIF(S) = det(R_SS) det(R_notS,notS) / det(R)
GSIF(S) = GVIF(S)^(1 / (2m))
For one coefficient, GVIF is the ordinary VIF and GSIF is its square-root standard-error inflation factor. Categorical terms are grouped by their original moderator. The library reports values without applying an automatic 5-or-10 cutoff.
Condition diagnostics use the actual fitted precision weights. Let D contain
the Euclidean column norms of W^(1/2) X, and define:
Z = W^(1/2) X D^(-1)
Z = U diag(s_1, ..., s_p) V'
condition_index_d = s_1 / s_d
For coefficient j and singular dimension d, the variance-decomposition
proportion is:
component_jd = V[j,d]^2 / s_d^2
proportion_jd = component_jd / sum_l(component_jl)
Column normalization makes these condition indices invariant to positive changes of moderator units. The original unweighted design condition number is also returned separately and remains scale-dependent.
A dimension is marked concerning only when its condition index exceeds 30
and more than 50% of the variance of at least two coefficients is concentrated
in that dimension. These conventional references are descriptive screening
heuristics; no term is removed or model refitted automatically.
Meta-regression linear contrasts¶
Let C be a user-supplied, full-row-rank matrix with one column per fitted
coefficient, and let d be the supplied rhs vector. With fitted coefficient
vector beta_hat and its selected normal or Hartung-Knapp covariance Sigma:
theta_hat = C beta_hat
Cov(theta_hat) = C Sigma C'
SE(theta_hat_j) = sqrt(Cov(theta_hat)[j,j])
test_j = (theta_hat_j - d_j) / SE(theta_hat_j)
Individual tests use the same distribution as coefficient inference: standard
normal for inference_method="normal" and t_(k-p) for either Hartung-Knapp
choice. Confidence intervals are centered on theta_hat, not on
theta_hat - d.
For q contrast rows, the joint Wald quantity is:
W = (C beta_hat - d)' (C Sigma C')^(-1) (C beta_hat - d)
Normal inference compares W with chi-squared on q degrees of freedom.
Hartung-Knapp inference reports W/q against F_(q,k-p). The implementation
uses a linear solve and rejects a rank-deficient contrast matrix instead of
using a pseudo-inverse.
Term-name mappings and labeled DataFrames are expanded to the complete fitted term order with zero weight for unspecified terms. Individual p-values are reported without multiplicity adjustment. The joint test evaluates the whole prespecified set but is not a substitute for a protocol-appropriate multiplicity strategy.
Binary study effects¶
For a treatment/control 2-by-2 table:
| Event | No event | |
|---|---|---|
| Treatment | a |
b |
| Control | c |
d |
Odds ratio¶
y_i = log(a d / (b c))
v_i = 1/a + 1/b + 1/c + 1/d
Risk ratio¶
y_i = log((a / (a + b)) / (c / (c + d)))
v_i = 1/a - 1/(a + b) + 1/c - 1/(c + d)
OR and RR results remain on the log model scale. Display properties exponentiate them. Continuity-correction and double-zero/double-all behavior is specified in zero-event studies.
Risk difference¶
With p_t = a / (a + b) and p_c = c / (c + d):
y_i = p_t - p_c
v_i = p_t (1 - p_t) / (a + b) + p_c (1 - p_c) / (c + d)
The effect always uses raw counts. The variance uses corrected counts for
every study selected by correction_scope; with the default
only_zero_studies scope, this includes every table containing a zero cell.
The separate rd_zero_variance policy determines whether tables whose raw
variance is exactly zero are corrected or excluded.
Mantel-Haenszel pooling¶
MH is implemented only for common-effect OR, RR, and RD. It uses raw tables by default and has a continuity-correction option separate from the study-effect correction. Every included stratum must have a positive total count; empty inputs and zero-total strata are rejected before ratio or variance arithmetic.
For OR, with n_i = a_i + b_i + c_i + d_i:
OR_MH = sum(a_i d_i / n_i) / sum(b_i c_i / n_i)
For RR, with treatment total n1_i = a_i + b_i and control total
n0_i = c_i + d_i:
RR_MH = sum(a_i n0_i / n_i) / sum(c_i n1_i / n_i)
The OR and RR model-scale estimates are the logarithms of the pooled ratios. Their standard errors use the Greenland-Robins equations implemented in the estimator.
For RD, define w_i = n1_i n0_i / n_i, W = sum(w_i), and
RD_i = a_i / n1_i - c_i / n0_i. The MH point estimate is:
RD_MH = sum(w_i RD_i) / W
Its Sato-Greenland-Robins sampling variance is calculated from:
A = sum(c_i (n1_i / n_i)^2
- a_i (n0_i / n_i)^2
+ (n1_i / n_i) (n0_i / n_i) (n0_i - n1_i) / 2)
B = sum(a_i (n0_i - c_i) / n_i
+ c_i (n1_i - a_i) / n_i)
Var(RD_MH) = (RD_MH A + B / 2) / W^2
RD remains on the identity scale and its normal confidence interval is not
clipped to [-1, 1]. The result records
mh_rd_variance="Sato-Greenland-Robins" in its method options.
An undefined raw OR/RR estimator or a non-positive RD sampling variance raises
InvalidStudyDataError. If the review protocol permits it, an explicit
positive mh_continuity_correction can make a sparse fit estimable; the
decision is recorded separately from the correction used for study effects.
Peto one-step odds ratio¶
Peto is implemented only for common-effect OR with a normal confidence
interval. For study i, let O_i = a_i, m_i = a_i + c_i, treatment and
control totals be n1_i and n0_i, and N_i = n1_i + n0_i. The expected
treatment events and their hypergeometric information are:
E_i = m_i n1_i / N_i
V_i = m_i (N_i - m_i) n1_i n0_i / (N_i^2 (N_i - 1))
The one-step study estimates and their variances are:
y_i = (O_i - E_i) / V_i
v_i = 1 / V_i
The pooled log odds ratio, its variance, and heterogeneity statistic are:
y_Peto = sum(O_i - E_i) / sum(V_i)
Var(y_Peto) = 1 / sum(V_i)
Q_Peto = sum(((O_i - E_i) - y_Peto V_i)^2 / V_i)
Normalized study weights are V_i / sum(V_i). Pooling and Q always use raw
tables; continuity_correction affects only the displayed one-step study
estimates and variances. Double-zero and double-all rows are excluded from all
synthesis calculations.
The approximation is intended for rare outcomes, similar treatment/control group sizes within studies, and effects that are not large. Every result records this caution. Peto is not an automatic replacement for MH or inverse-variance pooling when those conditions are doubtful. See ADR 0006.
Continuous study effects¶
Mean difference¶
y_i = mean_treat - mean_control
v_i = sd_treat^2 / n_treat + sd_control^2 / n_control
The variance is unpooled and does not require equal group variances.
Standardized mean difference¶
The pooled within-study variance and Cohen's d are:
df = n_treat + n_control - 2
s_pooled^2 = ((n_treat - 1) sd_treat^2
+ (n_control - 1) sd_control^2) / df
d = (mean_treat - mean_control) / s_pooled
PyMetaAnalysis applies the exact gamma-function correction:
J(df) = Gamma(df / 2) / (sqrt(df / 2) Gamma((df - 1) / 2))
g = J(df) d
The implemented LS sampling variance is:
v_i = 1/n_treat + 1/n_control + g^2 / (2 (n_treat + n_control))
Correlation study effects¶
meta_correlation(measure="ZCOR") transforms each independent Pearson
correlation with Fisher's r-to-z transformation:
y_i = atanh(r_i) = 0.5 log((1 + r_i) / (1 - r_i))
v_i = 1 / (n_i - 3)
The common- or random-effects model, heterogeneity statistics, confidence interval, and prediction interval are calculated on the Fisher's z scale. Display values use the inverse transformation:
r = tanh(z)
The accepted domain is -1 < r_i < 1 and integer n_i >= 4. The strict
correlation boundary prevents infinite transformed effects; the sample-size
boundary ensures a finite, strictly positive sampling variance. The pooled
display correlation is the inverse transform of the pooled z value, not a
direct arithmetic average of raw correlations.
This implementation assumes one independent correlation per study. It does not construct covariance matrices for multiple dependent correlations from the same sample. See ADR 0007.
Subgroup differences¶
Subgroup fits use the same selected model independently within each group. For random effects, tau-squared is estimated separately for every subgroup and the overall analysis whenever a subgroup contains at least two included studies. A single-study subgroup cannot identify tau-squared or Hartung-Knapp inference, so it is retained as that study's common-effect estimate and normal interval with an explicit warning. The overall fit remains random effects.
The formal subgroup-differences test weights subgroup pooled estimates by the inverse square of their classic model standard errors:
W_g = 1 / SE_g^2
theta_bar = sum(W_g theta_g) / sum(W_g)
Q_between = sum(W_g (theta_g - theta_bar)^2)
df_between = number_of_groups - 1
Q_between is compared with a chi-squared distribution. This is not a test
obtained by comparing separate subgroup p-values. Hartung-Knapp adjustments
affect subgroup confidence intervals but are not substituted for the classic
model variances in this Wald test.
Numerical and software references¶
The implementation is independent Python code. Regression fixtures generated
by R metafor, hand calculations, invariants, and numerical edge cases are
described in validation. Exact supported combinations and
defaults are listed in the API reference.