PyMetaAnalysis 0.3.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 (135) hide show
  1. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/.github/workflows/ci.yml +1 -1
  2. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/.github/workflows/pages.yml +1 -2
  3. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/.github/workflows/release.yml +7 -3
  4. pymetaanalysis-0.5.0/CHANGELOG.md +187 -0
  5. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/CITATION.cff +2 -2
  6. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/PKG-INFO +35 -7
  7. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/README.md +32 -5
  8. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/adr/0002-statistical-policy.md +7 -4
  9. pymetaanalysis-0.5.0/docs/adr/0003-meta-regression-prediction-intervals.md +70 -0
  10. pymetaanalysis-0.5.0/docs/adr/0004-hartung-knapp-prediction-intervals.md +46 -0
  11. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/getting-started.md +1 -1
  12. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/guides/input-data.md +18 -2
  13. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/guides/meta-regression.md +153 -3
  14. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/guides/method-selection.md +55 -4
  15. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/guides/r-interoperability.md +26 -4
  16. pymetaanalysis-0.5.0/docs/guides/sensitivity-analysis.md +198 -0
  17. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/guides/zero-events.md +19 -8
  18. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/index.md +6 -5
  19. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/limitations.md +19 -6
  20. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/methods/statistical-methods.md +208 -18
  21. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/reference/api.md +46 -16
  22. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/reference/report-schema.md +2 -1
  23. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/reference/results.md +188 -9
  24. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/releasing.md +14 -9
  25. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/validation.md +57 -13
  26. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/mkdocs.yml +2 -0
  27. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/pyproject.toml +2 -1
  28. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/__init__.py +16 -0
  29. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/_version.py +1 -1
  30. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/api.py +65 -17
  31. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/binary_api.py +30 -12
  32. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/continuous_api.py +37 -16
  33. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/data.py +40 -0
  34. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/design_matrix.py +80 -21
  35. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/effect_sizes/binary.py +60 -19
  36. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/effect_sizes/continuous.py +34 -4
  37. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/estimators/inverse_variance.py +22 -6
  38. pymetaanalysis-0.5.0/src/meta_analyze/estimators/mantel_haenszel.py +140 -0
  39. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/estimators/meta_regression.py +117 -34
  40. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/estimators/tau2.py +42 -20
  41. pymetaanalysis-0.5.0/src/meta_analyze/heterogeneity.py +285 -0
  42. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/plotting/_utils.py +10 -0
  43. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/plotting/forest.py +24 -5
  44. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/plotting/subgroup_forest.py +32 -0
  45. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/regression_api.py +56 -9
  46. pymetaanalysis-0.5.0/src/meta_analyze/regression_collinearity.py +285 -0
  47. pymetaanalysis-0.5.0/src/meta_analyze/regression_contrasts.py +365 -0
  48. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/regression_results.py +115 -1
  49. pymetaanalysis-0.5.0/src/meta_analyze/regression_sensitivity.py +563 -0
  50. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/reporting.py +28 -16
  51. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/results.py +170 -0
  52. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/sensitivity.py +115 -33
  53. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/subgroups.py +61 -12
  54. pymetaanalysis-0.5.0/tests/reference/README.md +77 -0
  55. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/reference/binary_metafor.json +12 -0
  56. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/reference/generate_binary_metafor.R +17 -1
  57. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/reference/generate_generic_metafor.R +32 -8
  58. pymetaanalysis-0.5.0/tests/reference/generate_meta_regression_collinearity_metafor.R +81 -0
  59. pymetaanalysis-0.5.0/tests/reference/generate_meta_regression_contrasts_metafor.R +96 -0
  60. pymetaanalysis-0.5.0/tests/reference/generate_meta_regression_influence_metafor.R +95 -0
  61. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/reference/generate_meta_regression_metafor.R +122 -9
  62. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/reference/generate_workflow_metafor.R +10 -0
  63. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/reference/generic_metafor.json +27 -1
  64. pymetaanalysis-0.5.0/tests/reference/meta_regression_collinearity_metafor.json +65 -0
  65. pymetaanalysis-0.5.0/tests/reference/meta_regression_contrasts_metafor.json +157 -0
  66. pymetaanalysis-0.5.0/tests/reference/meta_regression_influence_metafor.json +1131 -0
  67. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/reference/meta_regression_metafor.json +395 -1
  68. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/reference/workflow_metafor.json +6 -0
  69. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/test_api.py +236 -0
  70. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/test_binary.py +92 -0
  71. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/test_continuous.py +41 -0
  72. pymetaanalysis-0.5.0/tests/test_estimators.py +350 -0
  73. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/test_funnel_plot.py +1 -1
  74. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/test_meta_regression.py +160 -1
  75. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/test_numerical_stability.py +128 -0
  76. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/test_plotting.py +11 -0
  77. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/test_properties.py +85 -0
  78. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/test_r_references.py +271 -5
  79. pymetaanalysis-0.5.0/tests/test_regression_collinearity.py +288 -0
  80. pymetaanalysis-0.5.0/tests/test_regression_contrasts.py +346 -0
  81. pymetaanalysis-0.5.0/tests/test_regression_influence.py +362 -0
  82. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/test_regression_plotting.py +1 -1
  83. pymetaanalysis-0.5.0/tests/test_regression_sensitivity.py +298 -0
  84. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/test_release_readiness.py +39 -0
  85. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/test_reporting.py +5 -4
  86. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/test_sensitivity.py +84 -0
  87. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/test_subgroups.py +102 -8
  88. pymetaanalysis-0.3.0/CHANGELOG.md +0 -88
  89. pymetaanalysis-0.3.0/docs/guides/sensitivity-analysis.md +0 -108
  90. pymetaanalysis-0.3.0/src/meta_analyze/estimators/mantel_haenszel.py +0 -101
  91. pymetaanalysis-0.3.0/src/meta_analyze/heterogeneity.py +0 -99
  92. pymetaanalysis-0.3.0/tests/reference/README.md +0 -51
  93. pymetaanalysis-0.3.0/tests/test_estimators.py +0 -147
  94. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/.gitignore +0 -0
  95. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/CONTRIBUTING.md +0 -0
  96. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/LICENSE +0 -0
  97. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/SECURITY.md +0 -0
  98. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/benchmarks/README.md +0 -0
  99. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/benchmarks/benchmark_core.py +0 -0
  100. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/adr/0001-optional-matplotlib.md +0 -0
  101. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/citation.md +0 -0
  102. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/development.md +0 -0
  103. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/guides/binary-outcomes.md +0 -0
  104. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/guides/continuous-outcomes.md +0 -0
  105. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/guides/generic-effects.md +0 -0
  106. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/guides/plotting.md +0 -0
  107. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/guides/provenance-reporting.md +0 -0
  108. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/installation.md +0 -0
  109. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/docs/stylesheets/extra.css +0 -0
  110. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/examples/README.md +0 -0
  111. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/examples/meta_regression.ipynb +0 -0
  112. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/examples/quickstart.ipynb +0 -0
  113. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/config.py +0 -0
  114. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/effect_sizes/__init__.py +0 -0
  115. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/estimators/__init__.py +0 -0
  116. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/exceptions.py +0 -0
  117. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/plotting/__init__.py +0 -0
  118. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/plotting/funnel.py +0 -0
  119. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/plotting/regression.py +0 -0
  120. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/provenance.py +0 -0
  121. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/src/meta_analyze/py.typed +0 -0
  122. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/reference/binary_input.csv +0 -0
  123. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/reference/binary_sparse_input.csv +0 -0
  124. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/reference/continuous_input.csv +0 -0
  125. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/reference/continuous_metafor.json +0 -0
  126. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/reference/generate_continuous_metafor.R +0 -0
  127. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/reference/generic_input.csv +0 -0
  128. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/reference/meta_regression_boundary_input.csv +0 -0
  129. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/reference/meta_regression_input.csv +0 -0
  130. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/reference/workflow_input.csv +0 -0
  131. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/test_documentation.py +0 -0
  132. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tests/test_reference_results.py +0 -0
  133. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tools/check_release.py +0 -0
  134. {pymetaanalysis-0.3.0 → pymetaanalysis-0.5.0}/tools/execute_notebooks.py +0 -0
  135. {pymetaanalysis-0.3.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
@@ -0,0 +1,187 @@
1
+ # Changelog
2
+
3
+ All notable changes to PyMetaAnalysis will be documented in this file.
4
+
5
+ Changes planned for the next release accumulate under `Unreleased`.
6
+
7
+ ## Unreleased
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
+
88
+ ## 0.4.0 - 2026-07-23
89
+
90
+ ### Added
91
+
92
+ - leave-one-out Meta-regression refits with model-level diagnostics,
93
+ coefficient changes, explicit unidentifiable-deletion records, and preserved
94
+ provenance.
95
+ - exact Meta-regression externally standardized residuals, Cook's distances,
96
+ DFBETAS, transparent screening thresholds, and fixed-version R `metafor`
97
+ cross-software fixtures.
98
+ - Meta-regression term VIF, moderator-level GVIF/GSIF, and weighted,
99
+ column-scaled condition diagnostics with variance-decomposition proportions,
100
+ heuristic-only flags, and R `metafor` cross-software fixtures.
101
+ - explicit named Meta-regression linear contrasts with nonzero null values,
102
+ individual z/t inference, joint chi-squared/F tests, labeled coefficient
103
+ matrices, and R `metafor` cross-software fixtures.
104
+ - opt-in Riley Meta-regression true-effect prediction intervals using
105
+ `t_(k-p-1)`, with explicit residual-df validation, preserved refit
106
+ configuration, and fixed-version R `metafor` boundary references.
107
+
108
+ ## 0.3.0 - 2026-07-22
109
+
110
+ ### Added
111
+
112
+ - pandas-first `meta_regression()` for numeric, explicitly encoded categorical,
113
+ and multiple study-level moderators;
114
+ - common- and mixed-effects weighted regression with generalized DL, PM, and
115
+ REML residual tau-squared estimators;
116
+ - normal, Hartung-Knapp, and safeguarded Hartung-Knapp coefficient inference,
117
+ distribution-explicit moderator tests, residual heterogeneity, pseudo-R²,
118
+ prediction, provenance, and structured reports;
119
+ - optional weighted bubble plots for intercept-containing Meta-regression fits
120
+ with exactly one numeric moderator;
121
+ - independent R `metafor` fixtures covering numeric, categorical,
122
+ multivariable, zero-tau-squared, missing-row, and small-sample cases;
123
+ - an executable Meta-regression notebook plus a multivariable performance
124
+ baseline and expanded property, numerical-stability, and warning tests.
125
+
126
+ ### Changed
127
+
128
+ - report schema 1.2 adds the `meta_regression` report type.
129
+
130
+ ## 0.2.1 - 2026-07-17
131
+
132
+ ### Fixed
133
+
134
+ - README documentation and repository links use absolute URLs so they resolve
135
+ correctly when the project description is rendered on PyPI.
136
+
137
+ ## 0.2.0 - 2026-07-16
138
+
139
+ ### Added
140
+
141
+ - generic `meta_analysis()` accepts either sampling variances or standard
142
+ errors, with explicit validation and auditable conversion provenance.
143
+
144
+ ### Changed
145
+
146
+ - package author metadata identifies the project maintainer directly.
147
+
148
+ ### Fixed
149
+
150
+ - GitHub Release creation receives explicit repository context in tag-driven
151
+ release jobs.
152
+
153
+ ## 0.1.0 - 2026-07-15
154
+
155
+ ### Added
156
+
157
+ - pandas-first generic, binary, and continuous study-level meta-analysis APIs;
158
+ - common-effect and random-effects inverse-variance models;
159
+ - common-effect Mantel-Haenszel OR/RR pooling;
160
+ - REML, Paule-Mandel, and DerSimonian-Laird tau-squared estimators;
161
+ - normal, Hartung-Knapp, and safeguarded Hartung-Knapp confidence intervals;
162
+ - HTS random-effects prediction intervals;
163
+ - subgroup, leave-one-out, and cumulative workflows;
164
+ - optional Matplotlib forest, subgroup forest, and funnel plots;
165
+ - immutable results, diagnostics, provenance, Methods text, and JSON/Markdown
166
+ reports;
167
+ - R `metafor` cross-software fixtures, property tests, and numerical edge-case
168
+ coverage;
169
+ - explicit RD zero-variance boundary policy and heterogeneity-definition
170
+ reporting;
171
+ - complete MkDocs user, methods, API, validation, limitation, and development
172
+ documentation;
173
+ - R `meta`/`metafor` terminology and parameter mappings;
174
+ - machine-readable citation metadata and an executable end-to-end notebook;
175
+ - GitHub Pages and PyPI Trusted Publishing release workflows;
176
+ - release metadata, distribution-content, notebook-execution, and performance
177
+ baseline tooling.
178
+
179
+ ### Changed
180
+
181
+ - independent external statistical review is documented as a recommended
182
+ validation activity rather than a release requirement;
183
+ - report schema 1.1 records `heterogeneity.i2_method`;
184
+ - random-effects I-squared/H-squared use tau-squared and typical within-study
185
+ variance, while common-effect/MH analyses retain Q-based definitions;
186
+ - random-effects summaries provide method-selection notes for small-study and
187
+ positive-heterogeneity cases.
@@ -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.3.0
12
- date-released: 2026-07-22
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.3.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
@@ -28,7 +29,7 @@ Requires-Dist: actionlint-py>=1.7.12.24; extra == 'dev'
28
29
  Requires-Dist: build>=1.2; extra == 'dev'
29
30
  Requires-Dist: mypy>=1.10; extra == 'dev'
30
31
  Requires-Dist: pandas-stubs>=2.0; extra == 'dev'
31
- Requires-Dist: ruff>=0.6; extra == 'dev'
32
+ Requires-Dist: ruff<0.17,>=0.16; extra == 'dev'
32
33
  Requires-Dist: scipy-stubs>=1.10; extra == 'dev'
33
34
  Provides-Extra: docs
34
35
  Requires-Dist: mkdocs>=1.6; extra == 'docs'
@@ -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
@@ -177,11 +182,23 @@ Rows excluded by missing-value or sparse-data policies remain in
177
182
  `study_results` with a stable `row_id`, `included=False`, and an
178
183
  `exclusion_reason`.
179
184
 
180
- ## Sensitivity and plots
185
+ ## Diagnostics, contrasts, and plots
181
186
 
182
187
  ```python
183
188
  leave_one_out = result.leave_one_out().to_dataframe()
184
189
  cumulative = result.cumulative(order="publication_year").to_dataframe()
190
+ regression_deleted = regression.leave_one_out()
191
+ regression_coefficient_changes = regression_deleted.coefficients
192
+ regression_influence = regression.influence()
193
+ flagged_diagnostics = regression_influence.flagged
194
+ collinearity = regression.collinearity()
195
+ term_vif = collinearity.term_vif
196
+ moderator_gvif = collinearity.moderator_gvif
197
+ condition_indices = collinearity.condition_indices
198
+ south_vs_east = regression.contrast(
199
+ {"region[South]": 1.0, "region[East]": -1.0},
200
+ name="South - East",
201
+ )
185
202
 
186
203
  ax = result.forest(show_prediction_interval=True)
187
204
  ax = result.funnel()
@@ -189,9 +206,20 @@ ax = result.funnel()
189
206
 
190
207
  Plotting methods return Matplotlib axes and never call `show()`. Funnel plots
191
208
  are descriptive small-study-effect diagnostics, not proof of publication bias.
192
- An eligible single-numeric-moderator Meta-regression result additionally
193
- provides `regression.bubble()` with fitted confidence and optional prediction
194
- bands.
209
+ Meta-regression leave-one-out results also expose a long-form coefficient
210
+ change table. Exact influence diagnostics add externally standardized
211
+ residuals, Cook's distance, DFBETAS, and explicit heuristic screening
212
+ thresholds without automatically excluding studies. Meta-regression
213
+ collinearity diagnostics add `metafor`-compatible VIF/GVIF plus weighted,
214
+ column-scaled condition indices and variance-decomposition proportions.
215
+ Their documented references are review aids, not automatic variable-selection
216
+ rules. Explicit named linear contrasts provide individual z/t inference and
217
+ full-rank joint chi-squared/F tests without silently adjusting for multiple
218
+ testing. An eligible
219
+ single-numeric-moderator Meta-regression result additionally provides
220
+ `regression.bubble()` with fitted confidence and optional prediction bands.
221
+ Mixed-effects Meta-regression supports its documented default prediction rule
222
+ and an explicit Riley `t_(k-p-1)` alternative.
195
223
 
196
224
  ## Documentation
197
225
 
@@ -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
@@ -128,11 +132,23 @@ Rows excluded by missing-value or sparse-data policies remain in
128
132
  `study_results` with a stable `row_id`, `included=False`, and an
129
133
  `exclusion_reason`.
130
134
 
131
- ## Sensitivity and plots
135
+ ## Diagnostics, contrasts, and plots
132
136
 
133
137
  ```python
134
138
  leave_one_out = result.leave_one_out().to_dataframe()
135
139
  cumulative = result.cumulative(order="publication_year").to_dataframe()
140
+ regression_deleted = regression.leave_one_out()
141
+ regression_coefficient_changes = regression_deleted.coefficients
142
+ regression_influence = regression.influence()
143
+ flagged_diagnostics = regression_influence.flagged
144
+ collinearity = regression.collinearity()
145
+ term_vif = collinearity.term_vif
146
+ moderator_gvif = collinearity.moderator_gvif
147
+ condition_indices = collinearity.condition_indices
148
+ south_vs_east = regression.contrast(
149
+ {"region[South]": 1.0, "region[East]": -1.0},
150
+ name="South - East",
151
+ )
136
152
 
137
153
  ax = result.forest(show_prediction_interval=True)
138
154
  ax = result.funnel()
@@ -140,9 +156,20 @@ ax = result.funnel()
140
156
 
141
157
  Plotting methods return Matplotlib axes and never call `show()`. Funnel plots
142
158
  are descriptive small-study-effect diagnostics, not proof of publication bias.
143
- An eligible single-numeric-moderator Meta-regression result additionally
144
- provides `regression.bubble()` with fitted confidence and optional prediction
145
- bands.
159
+ Meta-regression leave-one-out results also expose a long-form coefficient
160
+ change table. Exact influence diagnostics add externally standardized
161
+ residuals, Cook's distance, DFBETAS, and explicit heuristic screening
162
+ thresholds without automatically excluding studies. Meta-regression
163
+ collinearity diagnostics add `metafor`-compatible VIF/GVIF plus weighted,
164
+ column-scaled condition indices and variance-decomposition proportions.
165
+ Their documented references are review aids, not automatic variable-selection
166
+ rules. Explicit named linear contrasts provide individual z/t inference and
167
+ full-rank joint chi-squared/F tests without silently adjusting for multiple
168
+ testing. An eligible
169
+ single-numeric-moderator Meta-regression result additionally provides
170
+ `regression.bubble()` with fitted confidence and optional prediction bands.
171
+ Mixed-effects Meta-regression supports its documented default prediction rule
172
+ and an explicit Riley `t_(k-p-1)` alternative.
146
173
 
147
174
  ## Documentation
148
175
 
@@ -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,70 @@
1
+ # ADR 0003: Meta-regression prediction-interval choices
2
+
3
+ - Status: Accepted
4
+ - Date: 2026-07-23
5
+
6
+ ## Context
7
+
8
+ For a mixed-effects Meta-regression prediction at design vector `x`, the
9
+ estimated true-effect variance combines residual heterogeneity with
10
+ uncertainty in the fitted mean:
11
+
12
+ ```text
13
+ Var_prediction = tau^2 + x' Cov(beta_hat) x
14
+ ```
15
+
16
+ The critical-value distribution remains a methodological choice. Common
17
+ software offers a default normal-or-t rule and a Riley alternative with one
18
+ fewer residual degree of freedom. Neither approximation removes uncertainty
19
+ from estimating tau-squared, and changing the package default would alter
20
+ existing results.
21
+
22
+ ## Decision
23
+
24
+ `meta_regression()` accepts
25
+ `prediction_interval_method="default" | "riley"` for mixed-effects models.
26
+ The canonical resolved method is stored in
27
+ `result.method.prediction_interval_method`.
28
+
29
+ The default remains `normal_or_t_k_minus_p`:
30
+
31
+ - normal coefficient inference uses a standard normal prediction critical
32
+ value;
33
+ - either Hartung-Knapp mode uses a t critical value with `k-p` degrees of
34
+ freedom.
35
+
36
+ The opt-in Riley method uses a t critical value with `k-p-1` degrees of
37
+ freedom regardless of the coefficient-inference distribution:
38
+
39
+ ```text
40
+ prediction = x' beta_hat
41
+ PI_Riley = prediction +/- t_(k-p-1) *
42
+ sqrt(tau^2 + x' Cov(beta_hat) x)
43
+ ```
44
+
45
+ Riley intervals require `k-p >= 2`. Requesting Riley for a common-effect model
46
+ or a mixed model without enough residual degrees of freedom raises a domain
47
+ error. The selection is preserved by deleted-study refits and reused by
48
+ `predict()` and `bubble()`.
49
+
50
+ Both rules predict the distribution of true effects in a new study at the
51
+ specified moderator values. They do not add an unknown sampling variance for
52
+ a future observed effect.
53
+
54
+ ## Validation
55
+
56
+ The committed fixed-version R `metafor` fixture records default and
57
+ `predtype="Riley"` predictions for normal and Hartung-Knapp inference,
58
+ multivariable moderator values, and a zero-tau-squared boundary. Unit tests
59
+ also check the critical-value formula and invalid degrees of freedom.
60
+ Property-based tests verify symmetry, unchanged mean-effect inference, and
61
+ the Riley interval's greater width relative to the default rule.
62
+
63
+ ## Consequences
64
+
65
+ - existing fits retain their numerical default;
66
+ - the alternative is explicit, auditable, and reproducible;
67
+ - Riley is documented as an alternative approximation rather than an
68
+ automatic small-sample correction;
69
+ - future prediction-interval rules require a separate statistical decision
70
+ and independent reference coverage.
@@ -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.
@@ -71,7 +71,7 @@ result.ci
71
71
  result.standard_error
72
72
  result.tau2
73
73
  result.q
74
- result.i2 # a proportion from 0 to 1
74
+ result.i2 # a proportion from 0 to 1
75
75
  result.h2
76
76
  result.i2_method
77
77
  ```
@@ -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: