Getting started

This guide fits a random-effects generic inverse-variance meta-analysis from a DataFrame. It also introduces the result object used by every analysis entry point.

1. Install the library

python -m pip install PyMetaAnalysis

For a source checkout, install the project in editable mode:

python -m pip install -e ".[test,plot]"

See installation for all extras and development checks.

2. Prepare a DataFrame

The generic API expects one effect estimate and either a strictly positive sampling variance or standard error per study. Supply variance= or standard_error=; standard errors are squared internally and recorded in provenance.

import pandas as pd

studies = pd.DataFrame(
    {
        "effect": [0.12, 0.35, -0.08, 0.21],
        "variance": [0.04, 0.06, 0.03, 0.05],
    },
    index=pd.Index(["Trial A", "Trial B", "Trial C", "Trial D"], name="study"),
)

When study= is omitted, a DataFrame's index becomes the display label. Pass a column name or array explicitly when the index is not the desired label.

3. Fit the model

import meta_analyze as ma

result = ma.meta_analysis(
    studies,
    effect="effect",
    variance="variance",
    model="random",
    tau2_method="REML",
)

print(result.summary())

The random-effects defaults are REML for between-study variance and a normal confidence interval for the pooled mean. Defaults are fully resolved in result.method; they do not need to be inferred from how the function was called.

4. Inspect the result

Scalar model results are available as named attributes:

result.estimate
result.ci
result.standard_error
result.tau2
result.q
result.i2  # a proportion from 0 to 1
result.h2
result.i2_method

The machine-readable summary and study table are suitable for reporting and further processing:

summary = result.summary().to_dict()

study_table = result.study_results[
    ["study", "effect", "variance", "included", "normalized_weight"]
]

study_results, excluded_studies, and to_dataframe() return defensive copies. Editing them does not mutate the fitted result.

5. Inspect provenance and create a report

Every result records how its inputs were resolved and which rows entered the fit:

result.provenance.column_mapping
result.provenance.included_rows
result.provenance.excluded_rows
result.provenance.transformations

Use method_details() for a concise Methods-style description. Use report() when a complete structured or human-readable export is needed:

methods_text = result.method_details()

report = result.report()
payload = report.to_dict()
json_text = report.to_json()
markdown = report.to_markdown()

See provenance and reporting for the report schema, strict JSON behavior, and subgroup reports.

6. Understand model and display scales

Generic effects, MD, SMD, and RD use the identity scale. OR and RR are modeled on a log scale, while ZCOR is modeled on Fisher's z scale. Their audit- friendly numeric attributes remain on those model scales. Use display properties for ratios and back-transformed correlations:

result.display_estimate
result.display_ci
result.display_prediction_interval

The binary-outcome guide includes a complete ratio example; the correlation guide explains the Fisher transformation.

7. Check sensitivity

Leave-one-out analysis refits the stored model once for every included study:

influence = result.leave_one_out()
print(influence.to_dataframe())

Cumulative analysis adds studies in input order by default:

cumulative = result.cumulative()
print(cumulative.to_dataframe())

The sensitivity-analysis guide explains minimum study counts, ordering, tied order values, and subgroup behavior.

8. Plot without implicit display

After installing the plot extra:

ax = result.forest(show_prediction_interval=True)
ax = result.funnel()
contour_ax = result.funnel(contour_levels=(0.90, 0.95, 0.99))
filled = result.trim_and_fill()
filled_ax = filled.funnel(contour_levels=(0.90, 0.95, 0.99))

Both methods return a Matplotlib Axes and never call show(). This keeps them usable in notebooks, scripts, tests, and composed figures. Funnel contours show two-sided significance regions around the null effect; they do not estimate missing studies.

The model-independent Begg-Mazumdar rank-correlation test and classical Egger regression test are available separately from plotting:

begg = result.begg_test()
egger = result.egger_test()
print(begg)
print(egger)

They test for funnel-plot asymmetry or small-study effects, not publication bias itself. Read small-study effects before using or interpreting them.

For a two-group binary odds-ratio result, the outcome-specific Harbord and Peters tests are also available:

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()
peters = binary_or_result.peters_test()

They require the retained event and group-size counts from meta_binary().

See plotting for every parameter, display-scale rules, axes composition, and interpretation cautions.

Next steps