PyMetaAnalysis 0.4.0__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/.github/workflows/ci.yml +1 -1
  2. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/.github/workflows/pages.yml +1 -2
  3. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/.github/workflows/release.yml +7 -3
  4. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/CHANGELOG.md +79 -0
  5. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/CITATION.cff +2 -2
  6. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/PKG-INFO +7 -2
  7. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/README.md +5 -1
  8. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/adr/0002-statistical-policy.md +7 -4
  9. pymetaanalysis-0.5.0/docs/adr/0004-hartung-knapp-prediction-intervals.md +46 -0
  10. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/guides/input-data.md +18 -2
  11. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/guides/meta-regression.md +4 -0
  12. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/guides/method-selection.md +55 -4
  13. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/guides/r-interoperability.md +14 -4
  14. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/guides/sensitivity-analysis.md +14 -6
  15. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/guides/zero-events.md +13 -2
  16. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/limitations.md +8 -2
  17. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/methods/statistical-methods.md +74 -17
  18. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/reference/api.md +32 -12
  19. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/reference/report-schema.md +2 -1
  20. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/reference/results.md +46 -7
  21. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/releasing.md +5 -0
  22. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/validation.md +45 -17
  23. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/mkdocs.yml +1 -0
  24. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/pyproject.toml +1 -0
  25. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/__init__.py +2 -0
  26. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/_version.py +1 -1
  27. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/api.py +65 -17
  28. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/binary_api.py +30 -12
  29. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/continuous_api.py +37 -16
  30. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/data.py +40 -0
  31. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/design_matrix.py +80 -21
  32. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/effect_sizes/binary.py +60 -19
  33. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/effect_sizes/continuous.py +34 -4
  34. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/estimators/inverse_variance.py +22 -6
  35. pymetaanalysis-0.5.0/src/meta_analyze/estimators/mantel_haenszel.py +140 -0
  36. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/estimators/meta_regression.py +117 -34
  37. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/estimators/tau2.py +42 -20
  38. pymetaanalysis-0.5.0/src/meta_analyze/heterogeneity.py +285 -0
  39. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/plotting/_utils.py +10 -0
  40. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/plotting/forest.py +24 -5
  41. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/plotting/subgroup_forest.py +32 -0
  42. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/regression_api.py +19 -6
  43. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/regression_collinearity.py +3 -22
  44. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/regression_results.py +48 -0
  45. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/regression_sensitivity.py +14 -38
  46. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/reporting.py +28 -16
  47. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/results.py +170 -0
  48. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/sensitivity.py +115 -33
  49. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/subgroups.py +61 -12
  50. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/README.md +23 -9
  51. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/binary_metafor.json +12 -0
  52. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/generate_binary_metafor.R +17 -1
  53. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/generate_generic_metafor.R +32 -8
  54. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/generate_meta_regression_influence_metafor.R +22 -8
  55. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/generate_meta_regression_metafor.R +83 -2
  56. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/generate_workflow_metafor.R +10 -0
  57. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/generic_metafor.json +27 -1
  58. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/meta_regression_influence_metafor.json +327 -0
  59. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/meta_regression_metafor.json +205 -0
  60. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/workflow_metafor.json +6 -0
  61. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_api.py +236 -0
  62. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_binary.py +92 -0
  63. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_continuous.py +41 -0
  64. pymetaanalysis-0.5.0/tests/test_estimators.py +350 -0
  65. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_funnel_plot.py +1 -1
  66. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_meta_regression.py +79 -0
  67. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_numerical_stability.py +128 -0
  68. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_plotting.py +11 -0
  69. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_properties.py +41 -0
  70. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_r_references.py +183 -5
  71. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_regression_influence.py +65 -22
  72. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_regression_plotting.py +1 -1
  73. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_regression_sensitivity.py +25 -0
  74. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_release_readiness.py +39 -0
  75. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_reporting.py +5 -4
  76. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_sensitivity.py +84 -0
  77. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_subgroups.py +102 -8
  78. pymetaanalysis-0.4.0/src/meta_analyze/estimators/mantel_haenszel.py +0 -101
  79. pymetaanalysis-0.4.0/src/meta_analyze/heterogeneity.py +0 -99
  80. pymetaanalysis-0.4.0/tests/test_estimators.py +0 -147
  81. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/.gitignore +0 -0
  82. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/CONTRIBUTING.md +0 -0
  83. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/LICENSE +0 -0
  84. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/SECURITY.md +0 -0
  85. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/benchmarks/README.md +0 -0
  86. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/benchmarks/benchmark_core.py +0 -0
  87. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/adr/0001-optional-matplotlib.md +0 -0
  88. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/adr/0003-meta-regression-prediction-intervals.md +0 -0
  89. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/citation.md +0 -0
  90. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/development.md +0 -0
  91. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/getting-started.md +0 -0
  92. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/guides/binary-outcomes.md +0 -0
  93. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/guides/continuous-outcomes.md +0 -0
  94. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/guides/generic-effects.md +0 -0
  95. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/guides/plotting.md +0 -0
  96. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/guides/provenance-reporting.md +0 -0
  97. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/index.md +0 -0
  98. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/installation.md +0 -0
  99. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/docs/stylesheets/extra.css +0 -0
  100. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/examples/README.md +0 -0
  101. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/examples/meta_regression.ipynb +0 -0
  102. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/examples/quickstart.ipynb +0 -0
  103. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/config.py +0 -0
  104. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/effect_sizes/__init__.py +0 -0
  105. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/estimators/__init__.py +0 -0
  106. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/exceptions.py +0 -0
  107. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/plotting/__init__.py +0 -0
  108. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/plotting/funnel.py +0 -0
  109. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/plotting/regression.py +0 -0
  110. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/provenance.py +0 -0
  111. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/py.typed +0 -0
  112. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/src/meta_analyze/regression_contrasts.py +0 -0
  113. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/binary_input.csv +0 -0
  114. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/binary_sparse_input.csv +0 -0
  115. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/continuous_input.csv +0 -0
  116. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/continuous_metafor.json +0 -0
  117. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/generate_continuous_metafor.R +0 -0
  118. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/generate_meta_regression_collinearity_metafor.R +0 -0
  119. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/generate_meta_regression_contrasts_metafor.R +0 -0
  120. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/generic_input.csv +0 -0
  121. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/meta_regression_boundary_input.csv +0 -0
  122. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/meta_regression_collinearity_metafor.json +0 -0
  123. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/meta_regression_contrasts_metafor.json +0 -0
  124. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/meta_regression_input.csv +0 -0
  125. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/reference/workflow_input.csv +0 -0
  126. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_documentation.py +0 -0
  127. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_reference_results.py +0 -0
  128. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_regression_collinearity.py +0 -0
  129. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tests/test_regression_contrasts.py +0 -0
  130. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tools/check_release.py +0 -0
  131. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tools/execute_notebooks.py +0 -0
  132. {pymetaanalysis-0.4.0 → pymetaanalysis-0.5.0}/tools/inspect_distribution.py +0 -0
@@ -22,7 +22,7 @@ jobs:
22
22
  strategy:
23
23
  fail-fast: false
24
24
  matrix:
25
- python-version: ["3.10", "3.11", "3.12", "3.13"]
25
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
26
26
 
27
27
  steps:
28
28
  - name: Check out repository
@@ -8,7 +8,7 @@ on:
8
8
 
9
9
  concurrency:
10
10
  group: pages
11
- cancel-in-progress: true
11
+ cancel-in-progress: false
12
12
 
13
13
  jobs:
14
14
  build:
@@ -17,7 +17,6 @@ jobs:
17
17
  timeout-minutes: 10
18
18
  permissions:
19
19
  contents: read
20
- pages: write
21
20
 
22
21
  steps:
23
22
  - name: Check out repository
@@ -35,14 +35,17 @@ jobs:
35
35
  cache: pip
36
36
  cache-dependency-path: pyproject.toml
37
37
 
38
- - name: Install build tools
38
+ - name: Install release dependencies
39
39
  run: |
40
40
  python -m pip install --upgrade pip
41
- python -m pip install build twine
41
+ python -m pip install ".[test]" build twine
42
42
 
43
43
  - name: Validate release metadata
44
44
  run: python tools/check_release.py --tag "$GITHUB_REF_NAME"
45
45
 
46
+ - name: Run tests with branch coverage
47
+ run: python -m pytest --cov=meta_analyze --cov-branch --cov-report=term-missing
48
+
46
49
  - name: Build distributions
47
50
  run: python -m build
48
51
 
@@ -91,7 +94,8 @@ jobs:
91
94
  path: dist/
92
95
 
93
96
  - name: Publish to PyPI
94
- uses: pypa/gh-action-pypi-publish@release/v1
97
+ # release/v1.14, pinned because this step holds the PyPI OIDC permission.
98
+ uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247
95
99
 
96
100
  github-release:
97
101
  name: Create GitHub Release
@@ -6,6 +6,85 @@ Changes planned for the next release accumulate under `Unreleased`.
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## 0.5.0 - 2026-07-25
10
+
11
+ ### Added
12
+
13
+ - random-effects inverse-variance results now provide opt-in Q-profile
14
+ confidence intervals for tau-squared, tau, I-squared, and H-squared, with an
15
+ explicit formal-empty-set flag at the constrained `[0, 0]` boundary.
16
+
17
+ ### Changed
18
+
19
+ - sensitivity and influence workflows now borrow internal fitted buffers during
20
+ refits instead of repeatedly materializing public defensive copies;
21
+ - Meta-regression stores its classic coefficient covariance alongside the
22
+ selected inference covariance and reuses one shared precision-geometry
23
+ implementation across fitting and diagnostics.
24
+ - independent `metafor` fixtures now cover categorical and multivariable
25
+ influence diagnostics, no-intercept Riley prediction intervals, and explicit
26
+ Mantel-Haenszel pooling correction; iterative failure paths have direct
27
+ regression tests.
28
+
29
+ ### Fixed
30
+
31
+ - inverse-variance means, heterogeneity statistics, and pooling and
32
+ meta-regression tau-squared equations now use overflow-safe relative-weight
33
+ calculations at the supported float64 boundary;
34
+ - subnormal variances and non-finite derived effects now raise explicit domain
35
+ errors instead of leaking runtime warnings or returning invalid results;
36
+ - binary OR, RD, and Mantel-Haenszel arithmetic now avoids intermediate
37
+ overflow for very large finite counts;
38
+ - the sensitivity guide no longer incorrectly states that Meta-regression
39
+ Cook's distance and DFBETAS are unavailable.
40
+ - subgroup-differences tests now use classic model variances independently of
41
+ Hartung-Knapp confidence-interval adjustments;
42
+ - leave-one-out and cumulative workflows now retain or skip, respectively,
43
+ reduced Mantel-Haenszel fits that are not estimable instead of aborting the
44
+ complete sensitivity analysis;
45
+ - forest and subgroup-forest plots now reject non-positive displayed
46
+ coordinates before enabling a logarithmic axis;
47
+ - uncorrected risk-ratio analyses now accept a zero non-event cell when the
48
+ study effect and sampling variance remain well defined.
49
+ - Hartung-Knapp random-effects prediction intervals now use the selected
50
+ adjusted pooled-mean variance and are recorded as `HK-PR`, matching
51
+ `metafor` Riley predictions;
52
+ - random-effects subgroup analyses now retain single-study subgroups through
53
+ an explicit, warned common-effect fallback instead of failing the complete
54
+ analysis;
55
+ - tagged releases now rerun the full branch-coverage test suite before
56
+ distributions can be built and published.
57
+ - tau-squared methods and SMD variance conventions now use `None` as the
58
+ context-sensitive default, so explicitly inapplicable settings raise domain
59
+ errors instead of being silently ignored;
60
+ - duplicate study labels now add a row-position warning while preserving
61
+ `row_id` as the unique audit key;
62
+ - report JSON now serializes `pd.NaT` study labels as `null` rather than the
63
+ string `"NaT"`.
64
+ - Meta-regression with `missing="drop"` now determines complete-row exclusions
65
+ before validating moderator values, so invalid values in already excluded
66
+ rows cannot abort the analysis;
67
+ - cumulative analysis now rejects ambiguous string `order` selectors that
68
+ exist in both source data and study results;
69
+ - empty inputs now report that at least one study row is required, and binary
70
+ zero-cell errors identify when `correction_scope="none"` disables an
71
+ otherwise positive correction.
72
+ - iterative tau-squared estimators now mark only an exact constrained zero as
73
+ a boundary solution, rather than treating every positive root below `atol`
74
+ as zero;
75
+ - the exported pooling and Meta-regression tau-squared estimators now reject
76
+ insufficient study or residual degrees of freedom with domain-specific
77
+ errors;
78
+ - the Mantel-Haenszel estimator now rejects empty and zero-total strata before
79
+ division, preventing NaN propagation and misleading variance diagnostics.
80
+ - prediction-interval metadata is now `None` when too few studies prevent an
81
+ interval from being calculated;
82
+ - categorical moderator encoding no longer conflates booleans, integers, and
83
+ floating-point values through Python's cross-type numeric equality;
84
+ - CI now covers Python 3.14, Pages deployments are not cancelled mid-flight,
85
+ and the credential-bearing PyPI publisher action is pinned to an immutable
86
+ commit.
87
+
9
88
  ## 0.4.0 - 2026-07-23
10
89
 
11
90
  ### Added
@@ -8,8 +8,8 @@ authors:
8
8
  - family-names: Ding
9
9
  given-names: Zhaobo
10
10
  email: ding.zb@yahoo.com
11
- version: 0.4.0
12
- date-released: 2026-07-23
11
+ version: 0.5.0
12
+ date-released: 2026-07-25
13
13
  repository-code: https://github.com/ZhaoboDing/PyMetaAnalysis
14
14
  url: https://zhaoboding.github.io/PyMetaAnalysis/
15
15
  license: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyMetaAnalysis
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: A pandas-first, auditable meta-analysis library for Python
5
5
  Project-URL: Documentation, https://zhaoboding.github.io/PyMetaAnalysis/
6
6
  Project-URL: Source, https://github.com/ZhaoboDing/PyMetaAnalysis
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.10
18
18
  Classifier: Programming Language :: Python :: 3.11
19
19
  Classifier: Programming Language :: Python :: 3.12
20
20
  Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
21
22
  Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
22
23
  Requires-Python: >=3.10
23
24
  Requires-Dist: numpy>=1.24
@@ -139,7 +140,10 @@ not individual-level or causal effects.
139
140
  Random-effects inverse-variance models support REML (default), Paule-Mandel,
140
141
  and DerSimonian-Laird tau-squared estimators. Mean confidence intervals support
141
142
  the normal default plus unmodified and safeguarded Hartung-Knapp variants.
142
- Eligible random-effects fits include an HTS prediction interval.
143
+ Eligible random-effects fits include an HTS prediction interval under normal
144
+ inference and an HK-PR interval under either Hartung-Knapp variant. Call
145
+ `result.tau2_confidence_interval()` for a Q-profile interval around
146
+ heterogeneity.
143
147
 
144
148
  Generic analyses accept exactly one of `variance=` or `standard_error=`.
145
149
  Standard errors are squared internally and the conversion is recorded in the
@@ -157,6 +161,7 @@ result.estimate
157
161
  result.display_estimate
158
162
  result.ci
159
163
  result.tau2
164
+ tau2_interval = result.tau2_confidence_interval()
160
165
  result.i2
161
166
  result.i2_method
162
167
  result.diagnostics
@@ -90,7 +90,10 @@ not individual-level or causal effects.
90
90
  Random-effects inverse-variance models support REML (default), Paule-Mandel,
91
91
  and DerSimonian-Laird tau-squared estimators. Mean confidence intervals support
92
92
  the normal default plus unmodified and safeguarded Hartung-Knapp variants.
93
- Eligible random-effects fits include an HTS prediction interval.
93
+ Eligible random-effects fits include an HTS prediction interval under normal
94
+ inference and an HK-PR interval under either Hartung-Knapp variant. Call
95
+ `result.tau2_confidence_interval()` for a Q-profile interval around
96
+ heterogeneity.
94
97
 
95
98
  Generic analyses accept exactly one of `variance=` or `standard_error=`.
96
99
  Standard errors are squared internally and the conversion is recorded in the
@@ -108,6 +111,7 @@ result.estimate
108
111
  result.display_estimate
109
112
  result.ci
110
113
  result.tau2
114
+ tau2_interval = result.tau2_confidence_interval()
111
115
  result.i2
112
116
  result.i2_method
113
117
  result.diagnostics
@@ -2,6 +2,8 @@
2
2
 
3
3
  - Status: Accepted
4
4
  - Date: 2026-07-15
5
+ - Amendment: the prediction-interval variance decision is superseded by
6
+ [ADR 0004](0004-hartung-knapp-prediction-intervals.md).
5
7
 
6
8
  ## Context
7
9
 
@@ -65,10 +67,11 @@ percentage in human-readable output.
65
67
 
66
68
  ### Prediction intervals
67
69
 
68
- Random-effects inverse-variance models use the Higgins-Thompson-Spiegelhalter
69
- prediction interval with `k - 2` degrees of freedom and the classic variance
70
- of the pooled mean. It is unavailable below three studies. With three or four
71
- studies it is calculated with an explicit uncertainty warning.
70
+ The original decision used the Higgins-Thompson-Spiegelhalter prediction
71
+ interval with `k - 2` degrees of freedom and the classic variance of the
72
+ pooled mean for every confidence-interval method. ADR 0004 replaces that
73
+ variance rule for Hartung-Knapp fits while retaining the study-count boundary
74
+ and small-sample warning.
72
75
 
73
76
  ## Validation
74
77
 
@@ -0,0 +1,46 @@
1
+ # ADR 0004: Hartung-Knapp prediction intervals
2
+
3
+ - Status: Accepted
4
+ - Date: 2026-07-24
5
+
6
+ ## Context
7
+
8
+ ADR 0002 applied the classic pooled-mean variance to every `k - 2`
9
+ random-effects prediction interval. That kept the interval independent of
10
+ `ci_method`, but it diverged from `metafor` Riley predictions and the
11
+ Hartung-Knapp Partlett-Riley option in R `meta`. Both use the covariance
12
+ selected for mean inference inside the prediction variance.
13
+
14
+ This difference was material when the unmodified Hartung-Knapp variance was
15
+ below or above the classic variance. The result metadata still identified the
16
+ interval as `HTS`, so callers could not discover the difference from method
17
+ configuration alone.
18
+
19
+ ## Decision
20
+
21
+ Random-effects inverse-variance prediction intervals retain the `k - 2`
22
+ critical value and three-study minimum:
23
+
24
+ ```text
25
+ mu_hat +/- t_(k - 2, 1 - alpha/2)
26
+ * sqrt(tau^2 + Var_selected(mu_hat))
27
+ ```
28
+
29
+ - normal inference uses the classic pooled-mean variance and records `HTS`;
30
+ - `hartung_knapp` uses its unmodified adjusted variance and records `HK-PR`;
31
+ - `hartung_knapp_adhoc` uses its lower-bounded adjusted variance and records
32
+ `HK-PR`.
33
+
34
+ Committed values are generated directly from
35
+ `metafor::predict(fit, predtype="Riley")` for all three inference choices.
36
+ Prediction intervals remain unavailable below three included studies and
37
+ retain the explicit warning with three or four studies.
38
+
39
+ ## Consequences
40
+
41
+ - prediction intervals and mean intervals use a coherent selected covariance;
42
+ - HK and safeguarded HK prediction intervals can differ from the normal HTS
43
+ interval even when tau-squared and the pooled estimate are unchanged;
44
+ - `result.method.prediction_interval_method` distinguishes `HTS` from
45
+ `HK-PR`;
46
+ - this decision supersedes only the prediction-variance paragraph of ADR 0002.
@@ -54,7 +54,10 @@ zero.
54
54
  `row_id` is the zero-based input position. It is independent of the study
55
55
  label and remains stable through exclusion, subgroup, leave-one-out, and
56
56
  cumulative workflows. Use it when study labels are duplicated or not suitable
57
- as identifiers.
57
+ as identifiers. Duplicate labels are accepted because several rows can
58
+ legitimately originate from the same publication, but the result records the
59
+ affected row positions in `warnings` so accidental duplication remains
60
+ auditable.
58
61
 
59
62
  The complete row table is available from:
60
63
 
@@ -84,6 +87,13 @@ result = ma.meta_analysis(
84
87
  result.excluded_studies[["row_id", "study", "exclusion_reason"]]
85
88
  ```
86
89
 
90
+ For Meta-regression, missingness is resolved across the outcome, uncertainty,
91
+ study label, and every moderator before moderator values are validated.
92
+ Consequently, a moderator value in an already excluded row is retained for
93
+ audit but cannot abort or influence the fitted design matrix. Moderator values
94
+ in included rows must still satisfy the declared numeric or categorical
95
+ contract.
96
+
87
97
  Dropped rows do not contribute to pooled estimates, Q, tau-squared, prediction
88
98
  intervals, or weights. Missing subgroup labels are always rejected because
89
99
  silently assigning or dropping them would change the subgroup definition.
@@ -92,7 +102,7 @@ silently assigning or dropping them would change the subgroup definition.
92
102
 
93
103
  | Input family | Required validation |
94
104
  | --- | --- |
95
- | Generic | finite effect; finite, strictly positive sampling variance |
105
+ | Generic | finite effect; finite, strictly positive sampling variance large enough for a finite float64 inverse weight |
96
106
  | Binary | integer event counts and totals; positive totals; `0 <= events <= total` |
97
107
  | Continuous | finite means/SDs; non-negative SDs; integer group sizes of at least 2 |
98
108
 
@@ -100,6 +110,12 @@ Binary and continuous APIs preserve their raw input columns in
100
110
  `study_results`. Derived effects, variances, correction indicators, and
101
111
  weights appear alongside them.
102
112
 
113
+ All numerical calculations use float64. Internally derived effects and
114
+ variances must remain finite, and every included sampling variance must be
115
+ large enough to produce a finite inverse-variance weight. Values outside that
116
+ representable range raise `InvalidStudyDataError` instead of returning
117
+ non-finite fitted results.
118
+
103
119
  ## Exclusion is visible
104
120
 
105
121
  Rows excluded by a configured rule remain present with:
@@ -67,6 +67,10 @@ This produces terms such as `region[Asia]` and `region[North America]`, each
67
67
  relative to `Europe`. The reference never depends on row order. Undeclared
68
68
  levels, levels absent after exclusions, and string moderators omitted from
69
69
  `categorical=` are errors rather than implicit recoding decisions.
70
+ Categorical matching preserves scalar kinds: Python and NumPy integers are
71
+ equivalent, for example, but booleans do not match integer levels and
72
+ floating-point values do not match integer levels. Declare levels using the
73
+ same scalar kind as the observed moderator values.
70
74
 
71
75
  Formula parsing, automatic interactions, splines, and polynomial terms are not
72
76
  implemented. Construct those columns explicitly before fitting when they are
@@ -17,6 +17,20 @@ variation is part of the estimand. A small number of studies makes
17
17
  between-study variance and interval estimation uncertain; it does not turn a
18
18
  random-effects question into a common-effect question.
19
19
 
20
+ The high-level APIs intentionally have outcome-specific defaults:
21
+
22
+ | API | Default model and pooling |
23
+ | --- | --- |
24
+ | `meta_analysis()` | random-effects inverse variance, REML |
25
+ | `meta_continuous()` | random-effects inverse variance, REML |
26
+ | `meta_binary()` | common-effect Mantel-Haenszel |
27
+ | `meta_regression()` | mixed-effects inverse variance, REML |
28
+
29
+ These defaults reflect common entry-point conventions, not an automatic model
30
+ choice. Switching outcome APIs can therefore switch the model family; set
31
+ `model` and, for binary data, `method` explicitly in analysis code and
32
+ protocols.
33
+
20
34
  ## Pooling methods
21
35
 
22
36
  | Data and model | Available pooling method |
@@ -42,9 +56,17 @@ Random-effects inverse-variance models provide:
42
56
 
43
57
  REML and PM are iterative. Convergence, iteration count, and whether the
44
58
  solution reached the zero boundary are recorded in `result.diagnostics`.
59
+ The boundary flag means the constrained estimate is exactly zero; a positive
60
+ root remains a positive estimate even when it is smaller than the configured
61
+ root-finding tolerance.
45
62
  Failure to converge raises `ConvergenceError`; it does not silently fall back
46
63
  to DL.
47
64
 
65
+ The public default is `tau2_method=None`: it resolves to REML for a random- or
66
+ mixed-effects fit. Common-effect and Mantel-Haenszel fits reject an explicitly
67
+ supplied tau-squared method so analysis code cannot appear to request an
68
+ estimator that was not used.
69
+
48
70
  ## Confidence intervals
49
71
 
50
72
  `ci_method="normal"` is the stable default and uses the classic normal
@@ -85,12 +107,36 @@ The definition is recorded as `result.i2_method`: `q_based` or
85
107
  `tau2_typical_variance`. Internally, I-squared remains a proportion from zero
86
108
  to one.
87
109
 
110
+ ## Heterogeneity confidence intervals
111
+
112
+ A random-effects inverse-variance fit can invert generalized Q to quantify
113
+ uncertainty around tau-squared:
114
+
115
+ ```python
116
+ heterogeneity_interval = result.tau2_confidence_interval()
117
+ heterogeneity_interval.ci
118
+ heterogeneity_interval.i2_ci
119
+ ```
120
+
121
+ The point estimate still follows the selected DL, PM, or REML estimator. The
122
+ Q-profile interval is a separate procedure and therefore is identical across
123
+ those point estimators for the same data and confidence level; it is not
124
+ guaranteed to contain every possible point estimate.
125
+
126
+ For unusually homogeneous data, both unconstrained bounds can lie below zero.
127
+ The method then has a formal empty confidence set. PyMetaAnalysis exposes
128
+ `is_empty=True` while representing the constrained tau-squared display as
129
+ `[0, 0]`; do not interpret that display as proof that heterogeneity is exactly
130
+ zero.
131
+
88
132
  ## Prediction intervals
89
133
 
90
- Random-effects models report an HTS prediction interval when at least three
91
- studies are included. It describes uncertainty for a new study's underlying
92
- effect, not uncertainty around the pooled mean. Common-effect models do not
93
- produce prediction intervals. With three or four studies the interval is still
134
+ Random-effects models report a prediction interval when at least three studies
135
+ are included. Under normal inference this is recorded as `HTS`; either
136
+ Hartung-Knapp choice supplies its selected pooled-mean variance and is recorded
137
+ as `HK-PR`. It describes uncertainty for a new study's underlying effect, not
138
+ uncertainty around the pooled mean. Common-effect models do not produce
139
+ prediction intervals. With three or four studies the interval is still
94
140
  calculated, but the result warns that it is especially uncertain.
95
141
 
96
142
  ## Subgroups
@@ -101,6 +147,11 @@ within each subgroup and again for the overall model. This is recorded as
101
147
  differences compares subgroup summary estimates; it is not a comparison of
102
148
  whether individual subgroup p-values are significant.
103
149
 
150
+ If a subgroup contains only one included study, tau-squared and random-effects
151
+ inference are not estimable within that subgroup. PyMetaAnalysis retains it as
152
+ the study's common-effect estimate and normal interval, records the fallback
153
+ in warnings, and continues the overall random-effects subgroup analysis.
154
+
104
155
  ## Reporting checklist
105
156
 
106
157
  At minimum, report:
@@ -69,7 +69,7 @@ remain explicit attributes in Python.
69
69
  | `model="random"` | random-effects `rma.uni()` | `random=TRUE` | Requires a tau-squared policy |
70
70
  | `method="IV"` | inverse-variance weighting | `method="Inverse"` | Binary API only; generic and continuous fits are IV |
71
71
  | `method="MH"` | `rma.mh()` | `method="MH"` | Common-effect OR/RR only |
72
- | `tau2_method="REML"` | `method="REML"` | `method.tau="REML"` | PyMetaAnalysis random-effects default |
72
+ | `tau2_method=None` (resolved as `"REML"`) | `method="REML"` | `method.tau="REML"` | PyMetaAnalysis random-effects default |
73
73
  | `tau2_method="PM"` | `method="PM"` | `method.tau="PM"` | Paule-Mandel |
74
74
  | `tau2_method="DL"` | `method="DL"` | `method.tau="DL"` | DerSimonian-Laird |
75
75
 
@@ -79,6 +79,13 @@ handling, or heterogeneity definitions. In particular, PyMetaAnalysis records
79
79
  tau-squared/typical-variance definition, while common-effect and MH results use
80
80
  the Q-based definition.
81
81
 
82
+ `result.tau2_confidence_interval()` corresponds to
83
+ `confint(fit, type="QP")` for an `rma.uni` random-effects fit. Both return
84
+ Q-profile bounds for tau-squared and monotonic tau, I-squared, and H-squared
85
+ transformations. PyMetaAnalysis additionally exposes `is_empty` so a formal
86
+ empty confidence set is distinguishable from its constrained `[0, 0]`
87
+ display.
88
+
82
89
  ## Confidence and prediction intervals
83
90
 
84
91
  | PyMetaAnalysis `ci_method` | `metafor` | R `meta` | Behavior |
@@ -87,9 +94,12 @@ the Q-based definition.
87
94
  | `"hartung_knapp"` | `test="knha"` | `method.random.ci="HK"` | Unmodified HK variance and t quantile |
88
95
  | `"hartung_knapp_adhoc"` | `test="adhoc"` | HK plus an explicitly selected ad hoc correction | HK variance cannot fall below the classic variance |
89
96
 
90
- Eligible random-effects fits include the documented HTS prediction interval.
91
- R packages offer additional prediction-interval choices, so matching the mean
92
- interval does not by itself guarantee a matching prediction interval.
97
+ Eligible random-effects fits use the documented HTS prediction interval under
98
+ normal inference. Either Hartung-Knapp choice instead uses its selected
99
+ pooled-mean variance with `k-2` degrees of freedom (`HK-PR`), matching
100
+ `predict(fit, predtype="Riley")` in `metafor`. R packages offer additional
101
+ prediction-interval choices, so method settings must still be compared
102
+ explicitly.
93
103
 
94
104
  Meta-regression uses a separate prediction rule. Its default corresponds to
95
105
  `predict(fit)` in `metafor`: normal inference uses a normal critical value and
@@ -18,13 +18,17 @@ print(influence.to_dataframe())
18
18
  The returned `LeaveOneOutResult` contains:
19
19
 
20
20
  - `original`, the fitted result supplied to the workflow;
21
- - `results`, one immutable refit per omitted included study;
21
+ - `results`, one omission-aligned immutable refit or `None` per included study;
22
22
  - `table`, `summary()`, and `to_dataframe()`, which return defensive copies;
23
+ - `failed`, the rows for reduced models that could not be estimated;
23
24
  - `warnings`, for workflow-level notes.
24
25
 
25
26
  The table identifies `omitted_row_id` and `omitted_study` and reports each
26
27
  refit's estimate, standard error, confidence interval, tau-squared, Q,
27
- I-squared, and H-squared.
28
+ I-squared, and H-squared. It also records `refit_success`, `error_type`, and
29
+ `error_message`. An unestimable deletion remains aligned in `results` as
30
+ `None`, retains a table row with unavailable numeric values, and does not stop
31
+ the remaining deletions.
28
32
 
29
33
  Originally excluded rows are never omission candidates. Common-effect analysis
30
34
  requires at least two included studies so each refit retains one. Random-effects
@@ -125,9 +129,17 @@ cumulative = result.cumulative(
125
129
  )
126
130
  ```
127
131
 
132
+ A string selector must identify exactly one namespace. If the same column name
133
+ exists in both the original source data and the result study table, it is
134
+ ambiguous and raises `InvalidStudyDataError`. Pass the intended Series or array
135
+ explicitly, for example `order=studies["publication_year"]`.
136
+
128
137
  `CumulativeMetaAnalysisResult.results` contains the estimable prefix fits, and
129
138
  `final` returns the last fit. Its table records the rows and study labels added
130
139
  at each step together with the same principal statistics as leave-one-out.
140
+ If an otherwise eligible prefix is not estimable, it is skipped with an
141
+ explicit warning. Its pending studies are included in the next estimable
142
+ prefix row, so the cumulative path and final all-study fit remain complete.
131
143
 
132
144
  When multiple studies have the same order value, `collapse=True` adds the tied
133
145
  studies in one step:
@@ -184,7 +196,3 @@ design, outcome definition, risk of bias, and numerical leverage. They are not
184
196
  by themselves a reason to exclude the study. Likewise, a cumulative trend can
185
197
  describe the historical evidence path but does not remove time-related changes
186
198
  in methods, populations, or publication processes.
187
-
188
- The current Meta-regression workflow reports exact deleted-model fits and
189
- coefficient changes. It does not yet calculate Cook's distance, DFBETAS, or an
190
- automatic influential-study flag.
@@ -28,6 +28,11 @@ Available scopes are:
28
28
  Setting the correction to zero or the scope to `none` is rejected when it
29
29
  leaves an OR/RR undefined or an effect variance non-positive.
30
30
 
31
+ For RR, a zero non-event cell is allowed when both arms still have positive
32
+ event counts and the sampling variance is positive. For example, a study with
33
+ events in every participant of one arm can be analyzed without correction.
34
+ OR still requires all four cells to be positive.
35
+
31
36
  ## Double-zero and double-all studies
32
37
 
33
38
  A study with no events in either group, or events in every participant in both
@@ -47,8 +52,14 @@ rd_zero_variance = "correct"
47
52
  ```
48
53
 
49
54
  The RD itself remains the raw treatment risk minus control risk. Corrected
50
- counts are used only to form a positive sampling variance. To exclude all such
51
- studies before pooling, Q, tau-squared, and weight calculations, use:
55
+ counts affect only its sampling variance. Under the default
56
+ `correction_scope="only_zero_studies"`, the corrected variance is used for
57
+ every retained RD table containing at least one zero cell, including
58
+ single-zero tables whose uncorrected variance was already positive. The
59
+ `rd_zero_variance` policy separately decides whether tables whose raw RD
60
+ variance is exactly zero are retained or excluded. To exclude all such
61
+ zero-variance studies before pooling, Q, tau-squared, and weight calculations,
62
+ use:
52
63
 
53
64
  ```python
54
65
  rd_zero_variance = "exclude"
@@ -12,9 +12,14 @@ deferred functionality explicit.
12
12
  - common-effect and univariate random-effects inverse-variance models;
13
13
  - common-effect Mantel-Haenszel OR/RR;
14
14
  - DL, PM, and REML tau-squared estimators;
15
+ - Q-profile confidence intervals for tau-squared and its monotonic tau,
16
+ I-squared, and H-squared transformations in random-effects inverse-variance
17
+ models;
15
18
  - normal and random-effects Hartung-Knapp mean intervals;
16
- - HTS prediction intervals;
17
- - independent subgroup fits and a formal subgroup-differences test;
19
+ - HTS and Hartung-Knapp Partlett-Riley (`HK-PR`) prediction intervals;
20
+ - independent subgroup fits and a formal subgroup-differences test, with a
21
+ warned common-effect representation when a random-effects subgroup contains
22
+ only one included study;
18
23
  - leave-one-out and cumulative repeated-fit workflows;
19
24
  - forest and descriptive funnel plots;
20
25
  - structured provenance and reports;
@@ -32,6 +37,7 @@ deferred functionality explicit.
32
37
  - Mantel-Haenszel risk differences or random-effects MH pooling;
33
38
  - formula parsing, automatic interactions/splines, stepwise moderator
34
39
  selection, automatic pairwise contrasts, or multiplicity correction;
40
+ - Q-profile confidence intervals for residual tau-squared in Meta-regression;
35
41
  - multilevel, multivariate, network, dose-response, diagnostic-accuracy, or
36
42
  individual-participant-data meta-analysis;
37
43
  - robust variance estimation or dependent-effect clustering;