PyMetaAnalysis 0.6.0__tar.gz → 0.7.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 (140) hide show
  1. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/CHANGELOG.md +46 -7
  2. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/CITATION.cff +2 -2
  3. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/PKG-INFO +5 -3
  4. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/README.md +4 -2
  5. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/benchmarks/README.md +5 -5
  6. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/benchmarks/benchmark_core.py +8 -0
  7. pymetaanalysis-0.7.0/docs/adr/0007-fisher-z-correlation.md +66 -0
  8. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/citation.md +1 -1
  9. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/getting-started.md +5 -3
  10. pymetaanalysis-0.7.0/docs/guides/correlation-outcomes.md +150 -0
  11. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/guides/input-data.md +3 -2
  12. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/guides/meta-regression.md +5 -4
  13. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/guides/method-selection.md +2 -0
  14. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/guides/plotting.md +3 -1
  15. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/guides/r-interoperability.md +11 -5
  16. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/guides/sensitivity-analysis.md +2 -2
  17. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/guides/zero-events.md +16 -7
  18. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/index.md +11 -9
  19. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/limitations.md +13 -2
  20. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/methods/statistical-methods.md +28 -0
  21. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/reference/api.md +56 -8
  22. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/reference/report-schema.md +10 -1
  23. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/reference/results.md +11 -4
  24. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/validation.md +6 -2
  25. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/mkdocs.yml +2 -0
  26. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/__init__.py +2 -0
  27. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/_version.py +1 -1
  28. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/binary_api.py +25 -9
  29. pymetaanalysis-0.7.0/src/meta_analyze/correlation_api.py +317 -0
  30. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/design_matrix.py +22 -5
  31. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/effect_sizes/__init__.py +8 -0
  32. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/effect_sizes/binary.py +3 -4
  33. pymetaanalysis-0.7.0/src/meta_analyze/effect_sizes/correlation.py +215 -0
  34. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/estimators/mantel_haenszel.py +3 -1
  35. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/estimators/peto.py +10 -5
  36. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/heterogeneity.py +8 -1
  37. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/plotting/_utils.py +3 -0
  38. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/reporting.py +7 -0
  39. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/results.py +2 -0
  40. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/sensitivity.py +26 -4
  41. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/subgroups.py +4 -1
  42. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/README.md +12 -1
  43. pymetaanalysis-0.7.0/tests/reference/correlation_input.csv +9 -0
  44. pymetaanalysis-0.7.0/tests/reference/correlation_metafor.json +27 -0
  45. pymetaanalysis-0.7.0/tests/reference/generate_correlation_metafor.R +59 -0
  46. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/generate_generic_metafor.R +10 -0
  47. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/generate_workflow_metafor.R +29 -0
  48. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/generic_metafor.json +7 -0
  49. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/workflow_metafor.json +14 -0
  50. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_binary.py +79 -2
  51. pymetaanalysis-0.7.0/tests/test_correlation.py +288 -0
  52. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_funnel_plot.py +20 -0
  53. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_meta_regression.py +42 -13
  54. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_plotting.py +24 -0
  55. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_properties.py +62 -0
  56. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_r_references.py +55 -1
  57. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_release_readiness.py +1 -0
  58. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_sensitivity.py +24 -0
  59. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_subgroups.py +15 -0
  60. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/.github/workflows/ci.yml +0 -0
  61. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/.github/workflows/pages.yml +0 -0
  62. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/.github/workflows/release.yml +0 -0
  63. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/.gitignore +0 -0
  64. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/CONTRIBUTING.md +0 -0
  65. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/LICENSE +0 -0
  66. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/SECURITY.md +0 -0
  67. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/adr/0001-optional-matplotlib.md +0 -0
  68. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/adr/0002-statistical-policy.md +0 -0
  69. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/adr/0003-meta-regression-prediction-intervals.md +0 -0
  70. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/adr/0004-hartung-knapp-prediction-intervals.md +0 -0
  71. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/adr/0005-mantel-haenszel-risk-difference.md +0 -0
  72. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/adr/0006-peto-odds-ratio.md +0 -0
  73. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/development.md +0 -0
  74. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/guides/binary-outcomes.md +0 -0
  75. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/guides/continuous-outcomes.md +0 -0
  76. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/guides/generic-effects.md +0 -0
  77. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/guides/provenance-reporting.md +0 -0
  78. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/installation.md +0 -0
  79. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/releasing.md +0 -0
  80. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/docs/stylesheets/extra.css +0 -0
  81. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/examples/README.md +0 -0
  82. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/examples/meta_regression.ipynb +0 -0
  83. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/examples/quickstart.ipynb +0 -0
  84. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/pyproject.toml +0 -0
  85. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/api.py +0 -0
  86. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/config.py +0 -0
  87. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/continuous_api.py +0 -0
  88. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/data.py +0 -0
  89. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/effect_sizes/continuous.py +0 -0
  90. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/estimators/__init__.py +0 -0
  91. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/estimators/inverse_variance.py +0 -0
  92. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/estimators/meta_regression.py +0 -0
  93. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/estimators/tau2.py +0 -0
  94. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/exceptions.py +0 -0
  95. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/plotting/__init__.py +0 -0
  96. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/plotting/forest.py +0 -0
  97. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/plotting/funnel.py +0 -0
  98. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/plotting/regression.py +0 -0
  99. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/plotting/subgroup_forest.py +0 -0
  100. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/provenance.py +0 -0
  101. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/py.typed +0 -0
  102. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/regression_api.py +0 -0
  103. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/regression_collinearity.py +0 -0
  104. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/regression_contrasts.py +0 -0
  105. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/regression_results.py +0 -0
  106. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/src/meta_analyze/regression_sensitivity.py +0 -0
  107. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/binary_input.csv +0 -0
  108. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/binary_metafor.json +0 -0
  109. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/binary_sparse_input.csv +0 -0
  110. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/continuous_input.csv +0 -0
  111. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/continuous_metafor.json +0 -0
  112. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/generate_binary_metafor.R +0 -0
  113. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/generate_continuous_metafor.R +0 -0
  114. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/generate_meta_regression_collinearity_metafor.R +0 -0
  115. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/generate_meta_regression_contrasts_metafor.R +0 -0
  116. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/generate_meta_regression_influence_metafor.R +0 -0
  117. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/generate_meta_regression_metafor.R +0 -0
  118. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/generic_input.csv +0 -0
  119. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/meta_regression_boundary_input.csv +0 -0
  120. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/meta_regression_collinearity_metafor.json +0 -0
  121. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/meta_regression_contrasts_metafor.json +0 -0
  122. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/meta_regression_influence_metafor.json +0 -0
  123. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/meta_regression_input.csv +0 -0
  124. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/meta_regression_metafor.json +0 -0
  125. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/reference/workflow_input.csv +0 -0
  126. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_api.py +0 -0
  127. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_continuous.py +0 -0
  128. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_documentation.py +0 -0
  129. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_estimators.py +0 -0
  130. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_numerical_stability.py +0 -0
  131. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_reference_results.py +0 -0
  132. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_regression_collinearity.py +0 -0
  133. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_regression_contrasts.py +0 -0
  134. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_regression_influence.py +0 -0
  135. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_regression_plotting.py +0 -0
  136. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_regression_sensitivity.py +0 -0
  137. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tests/test_reporting.py +0 -0
  138. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tools/check_release.py +0 -0
  139. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tools/execute_notebooks.py +0 -0
  140. {pymetaanalysis-0.6.0 → pymetaanalysis-0.7.0}/tools/inspect_distribution.py +0 -0
@@ -6,6 +6,37 @@ Changes planned for the next release accumulate under `Unreleased`.
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## 0.7.0 - 2026-09-02
10
+
11
+ ### Added
12
+
13
+ - `meta_correlation()` for independent study-level correlations using Fisher's
14
+ r-to-z transformation (`ZCOR`), `1 / (n - 3)` sampling variances, common- or
15
+ REML/PM/DL random-effects inverse-variance pooling, back-transformed result
16
+ displays, subgroup and sensitivity workflows, plotting, provenance, reports,
17
+ and independent R `metafor` references.
18
+
19
+ ### Changed
20
+
21
+ - integer-valued floating-point categorical moderators now match declared
22
+ integer levels while booleans remain distinct, supporting pandas columns
23
+ promoted to floating point by missing values;
24
+ - MH pooling corrections now use `None` as the context-sensitive scope default;
25
+ explicitly supplying either MH-only option to IV or Peto pooling raises an
26
+ error, and non-MH method metadata no longer records unused MH settings;
27
+ - estimator documentation now makes the pooled-mean-only Q-profile contract,
28
+ the prefiltered `fit_peto()` input contract, and sparse MH RD boundary policy
29
+ explicit;
30
+ - fixed-version `metafor` references now cover a two-study Hartung-Knapp
31
+ interval and Q-profile intervals reached through random-effects subgroups.
32
+
33
+ ### Fixed
34
+
35
+ - normal-inference subgroup tests reuse each fitted standard error directly,
36
+ avoiding an unnecessary recomputation and last-bit numerical drift.
37
+ - Peto observed-minus-expected arithmetic now uses a treatment/control-symmetric
38
+ formulation, preventing avoidable last-bit drift for highly imbalanced arms.
39
+
9
40
  ## 0.6.0 - 2026-08-13
10
41
 
11
42
  ### Added
@@ -43,6 +74,21 @@ Changes planned for the next release accumulate under `Unreleased`.
43
74
  Mantel-Haenszel pooling correction; iterative failure paths have direct
44
75
  regression tests.
45
76
 
77
+ ### Breaking changes
78
+
79
+ - explicitly supplying `tau2_method` to a common-effect model, including the
80
+ former default spelling `"REML"`, now raises an error instead of being
81
+ ignored; explicitly inapplicable SMD variance settings follow the same rule;
82
+ - cumulative analysis now rejects a string `order` selector when the name is
83
+ present in both source data and the calculated study table, instead of
84
+ silently preferring the source-data column;
85
+ - `LeaveOneOutResult.results` now preserves failed refits as `None`, and its
86
+ table adds `refit_success`, `error_type`, and `error_message` columns;
87
+ - pooled-result `prediction_interval_method` metadata now uses `null` when an
88
+ interval is unavailable and `"HK-PR"` for Hartung-Knapp intervals. Report
89
+ schema 1.2 remains unchanged because the field itself was already present;
90
+ consumers must handle the documented value set.
91
+
46
92
  ### Fixed
47
93
 
48
94
  - inverse-variance means, heterogeneity statistics, and pooling and
@@ -71,9 +117,6 @@ Changes planned for the next release accumulate under `Unreleased`.
71
117
  analysis;
72
118
  - tagged releases now rerun the full branch-coverage test suite before
73
119
  distributions can be built and published.
74
- - tau-squared methods and SMD variance conventions now use `None` as the
75
- context-sensitive default, so explicitly inapplicable settings raise domain
76
- errors instead of being silently ignored;
77
120
  - duplicate study labels now add a row-position warning while preserving
78
121
  `row_id` as the unique audit key;
79
122
  - report JSON now serializes `pd.NaT` study labels as `null` rather than the
@@ -81,8 +124,6 @@ Changes planned for the next release accumulate under `Unreleased`.
81
124
  - Meta-regression with `missing="drop"` now determines complete-row exclusions
82
125
  before validating moderator values, so invalid values in already excluded
83
126
  rows cannot abort the analysis;
84
- - cumulative analysis now rejects ambiguous string `order` selectors that
85
- exist in both source data and study results;
86
127
  - empty inputs now report that at least one study row is required, and binary
87
128
  zero-cell errors identify when `correction_scope="none"` disables an
88
129
  otherwise positive correction.
@@ -94,8 +135,6 @@ Changes planned for the next release accumulate under `Unreleased`.
94
135
  errors;
95
136
  - the Mantel-Haenszel estimator now rejects empty and zero-total strata before
96
137
  division, preventing NaN propagation and misleading variance diagnostics.
97
- - prediction-interval metadata is now `None` when too few studies prevent an
98
- interval from being calculated;
99
138
  - categorical moderator encoding no longer conflates booleans, integers, and
100
139
  floating-point values through Python's cross-type numeric equality;
101
140
  - CI now covers Python 3.14, Pages deployments are not cancelled mid-flight,
@@ -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.6.0
12
- date-released: 2026-08-13
11
+ version: 0.7.0
12
+ date-released: 2026-09-02
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.6.0
3
+ Version: 0.7.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
@@ -135,6 +135,7 @@ not individual-level or causal effects.
135
135
  | Effect + sampling variance or standard error | Generic | Common/random inverse variance |
136
136
  | Two-group events + totals | OR, RR, RD | Common MH; common Peto OR; common/random IV |
137
137
  | Two-group means + SDs + sizes | MD, Hedges' g | Common/random inverse variance |
138
+ | Correlations + sample sizes | Fisher's z (`ZCOR`) | Common/random inverse variance |
138
139
  | Effect + variance/SE + moderators | Generic | Common/mixed Meta-regression |
139
140
 
140
141
  Random-effects inverse-variance models support REML (default), Paule-Mandel,
@@ -182,7 +183,8 @@ markdown = report.to_markdown()
182
183
 
183
184
  OR and RR remain on the log model scale in auditable numeric attributes;
184
185
  `display_estimate`, `display_ci`, and `display_prediction_interval` provide
185
- exponentiated ratios.
186
+ exponentiated ratios. `ZCOR` results similarly retain Fisher's z internally
187
+ and expose back-transformed correlations through the display properties.
186
188
 
187
189
  Rows excluded by missing-value or sparse-data policies remain in
188
190
  `study_results` with a stable `row_id`, `included=False`, and an
@@ -235,7 +237,7 @@ The complete documentation is published at
235
237
  - [Installation](https://zhaoboding.github.io/PyMetaAnalysis/installation/)
236
238
  - [Getting started](https://zhaoboding.github.io/PyMetaAnalysis/getting-started/)
237
239
  - [Input data and row decisions](https://zhaoboding.github.io/PyMetaAnalysis/guides/input-data/)
238
- - [Generic](https://zhaoboding.github.io/PyMetaAnalysis/guides/generic-effects/), [binary](https://zhaoboding.github.io/PyMetaAnalysis/guides/binary-outcomes/), and [continuous](https://zhaoboding.github.io/PyMetaAnalysis/guides/continuous-outcomes/) guides
240
+ - [Generic](https://zhaoboding.github.io/PyMetaAnalysis/guides/generic-effects/), [binary](https://zhaoboding.github.io/PyMetaAnalysis/guides/binary-outcomes/), [continuous](https://zhaoboding.github.io/PyMetaAnalysis/guides/continuous-outcomes/), and [correlation](https://zhaoboding.github.io/PyMetaAnalysis/guides/correlation-outcomes/) guides
239
241
  - [Meta-regression](https://zhaoboding.github.io/PyMetaAnalysis/guides/meta-regression/)
240
242
  - [Choosing methods](https://zhaoboding.github.io/PyMetaAnalysis/guides/method-selection/) and [statistical formulas](https://zhaoboding.github.io/PyMetaAnalysis/methods/statistical-methods/)
241
243
  - [Sensitivity analysis](https://zhaoboding.github.io/PyMetaAnalysis/guides/sensitivity-analysis/) and [plotting](https://zhaoboding.github.io/PyMetaAnalysis/guides/plotting/)
@@ -85,6 +85,7 @@ not individual-level or causal effects.
85
85
  | Effect + sampling variance or standard error | Generic | Common/random inverse variance |
86
86
  | Two-group events + totals | OR, RR, RD | Common MH; common Peto OR; common/random IV |
87
87
  | Two-group means + SDs + sizes | MD, Hedges' g | Common/random inverse variance |
88
+ | Correlations + sample sizes | Fisher's z (`ZCOR`) | Common/random inverse variance |
88
89
  | Effect + variance/SE + moderators | Generic | Common/mixed Meta-regression |
89
90
 
90
91
  Random-effects inverse-variance models support REML (default), Paule-Mandel,
@@ -132,7 +133,8 @@ markdown = report.to_markdown()
132
133
 
133
134
  OR and RR remain on the log model scale in auditable numeric attributes;
134
135
  `display_estimate`, `display_ci`, and `display_prediction_interval` provide
135
- exponentiated ratios.
136
+ exponentiated ratios. `ZCOR` results similarly retain Fisher's z internally
137
+ and expose back-transformed correlations through the display properties.
136
138
 
137
139
  Rows excluded by missing-value or sparse-data policies remain in
138
140
  `study_results` with a stable `row_id`, `included=False`, and an
@@ -185,7 +187,7 @@ The complete documentation is published at
185
187
  - [Installation](https://zhaoboding.github.io/PyMetaAnalysis/installation/)
186
188
  - [Getting started](https://zhaoboding.github.io/PyMetaAnalysis/getting-started/)
187
189
  - [Input data and row decisions](https://zhaoboding.github.io/PyMetaAnalysis/guides/input-data/)
188
- - [Generic](https://zhaoboding.github.io/PyMetaAnalysis/guides/generic-effects/), [binary](https://zhaoboding.github.io/PyMetaAnalysis/guides/binary-outcomes/), and [continuous](https://zhaoboding.github.io/PyMetaAnalysis/guides/continuous-outcomes/) guides
190
+ - [Generic](https://zhaoboding.github.io/PyMetaAnalysis/guides/generic-effects/), [binary](https://zhaoboding.github.io/PyMetaAnalysis/guides/binary-outcomes/), [continuous](https://zhaoboding.github.io/PyMetaAnalysis/guides/continuous-outcomes/), and [correlation](https://zhaoboding.github.io/PyMetaAnalysis/guides/correlation-outcomes/) guides
189
191
  - [Meta-regression](https://zhaoboding.github.io/PyMetaAnalysis/guides/meta-regression/)
190
192
  - [Choosing methods](https://zhaoboding.github.io/PyMetaAnalysis/guides/method-selection/) and [statistical formulas](https://zhaoboding.github.io/PyMetaAnalysis/methods/statistical-methods/)
191
193
  - [Sensitivity analysis](https://zhaoboding.github.io/PyMetaAnalysis/guides/sensitivity-analysis/) and [plotting](https://zhaoboding.github.io/PyMetaAnalysis/guides/plotting/)
@@ -1,10 +1,10 @@
1
1
  # Core performance baseline
2
2
 
3
- `benchmark_core.py` measures representative generic, binary, continuous, and
4
- multivariable Meta-regression random-effects fits using deterministic synthetic
5
- data. Correctness remains the primary performance requirement; this benchmark
6
- is intended to reveal large regressions, not to enforce a fragile wall-clock
7
- threshold.
3
+ `benchmark_core.py` measures representative generic, binary, continuous,
4
+ correlation, and multivariable Meta-regression random-effects fits using
5
+ deterministic synthetic data. Correctness remains the primary performance
6
+ requirement; this benchmark is intended to reveal large regressions, not to
7
+ enforce a fragile wall-clock threshold.
8
8
 
9
9
  Run the default benchmark with:
10
10
 
@@ -48,6 +48,8 @@ def _cases(studies: int) -> dict[str, Callable[[], object]]:
48
48
  mean_treat = mean_control + rng.normal(0.25, 0.18, size=studies)
49
49
  sd_treat = rng.uniform(0.7, 1.6, size=studies)
50
50
  sd_control = rng.uniform(0.7, 1.6, size=studies)
51
+ correlation = rng.uniform(-0.65, 0.65, size=studies)
52
+ correlation_n = rng.integers(20, 300, size=studies)
51
53
 
52
54
  return {
53
55
  "generic_random_reml": lambda: ma.meta_analysis(
@@ -77,6 +79,12 @@ def _cases(studies: int) -> dict[str, Callable[[], object]]:
77
79
  model="random",
78
80
  tau2_method="REML",
79
81
  ),
82
+ "correlation_random_reml": lambda: ma.meta_correlation(
83
+ correlation=correlation,
84
+ n=correlation_n,
85
+ model="random",
86
+ tau2_method="REML",
87
+ ),
80
88
  "meta_regression_multivariable_reml": lambda: ma.meta_regression(
81
89
  effect=generic_effect,
82
90
  variance=generic_variance,
@@ -0,0 +1,66 @@
1
+ # ADR 0007: Fisher's z correlation pooling
2
+
3
+ - Status: Accepted
4
+ - Date: 2026-09-02
5
+
6
+ ## Context
7
+
8
+ Study-level correlations are common in behavioral, educational, and medical
9
+ research. Directly pooling raw correlations is possible but their sampling
10
+ distribution is bounded, asymmetric away from zero, and has a variance that
11
+ depends on the underlying correlation. Fisher's r-to-z transformation is the
12
+ standard first implementation in R `meta` and `metafor` and composes with the
13
+ library's existing inverse-variance machinery.
14
+
15
+ The API also needs an explicit position on perfect correlations, very small
16
+ samples, displayed values, and multiple correlations drawn from the same
17
+ participants.
18
+
19
+ ## Decision
20
+
21
+ `meta_correlation()` accepts raw `correlation` and sample size `n`, but its
22
+ first supported measure is only `ZCOR`:
23
+
24
+ ```text
25
+ y_i = atanh(r_i)
26
+ v_i = 1 / (n_i - 3)
27
+ ```
28
+
29
+ All fitting and heterogeneity calculations use Fisher's z. Result display
30
+ properties and plots use `tanh` to return the correlation scale. The result
31
+ records `effect_scale="fisher_z"`, `display_scale="tanh"`, the transformation,
32
+ variance equation, affected rows, and input sources.
33
+
34
+ Included correlations must be finite and strictly between -1 and 1. Sample
35
+ sizes must be whole numbers of at least 4. The implementation does not clip
36
+ perfect correlations or replace invalid sample sizes because either action
37
+ would introduce an undocumented effect or variance.
38
+
39
+ The default is the library's random-effects inverse-variance policy: REML
40
+ tau-squared and a normal confidence interval. Common effects, PM/DL, the two
41
+ documented Hartung-Knapp options, shared prediction intervals, Q-profile
42
+ heterogeneity intervals, subgroups, repeated-fit sensitivity, reports, and
43
+ plots reuse their existing contracts.
44
+
45
+ Rows are assumed independent. Duplicate labels remain allowed and warned, but
46
+ do not imply a dependence correction. Raw-correlation pooling (`COR`),
47
+ dependent correlations, partial/rank correlations, and reliability
48
+ corrections are deferred.
49
+
50
+ ## Validation
51
+
52
+ Committed fixtures generated by `metafor::escalc(measure="ZCOR")` and
53
+ `rma.uni()` validate study effects, variances, common-effect and REML fits,
54
+ weights, and back-transformation. Targeted and property-based tests cover
55
+ domain boundaries, missing rows, sign symmetry, row-order invariance,
56
+ subgroups, sensitivity refits, reports, and Matplotlib coordinates.
57
+
58
+ ## Consequences
59
+
60
+ - users can supply pandas columns without calculating z values or variances;
61
+ - model-scale and correlation-scale results remain distinguishable;
62
+ - behavior matches a documented R workflow without importing R at runtime;
63
+ - perfect correlations and samples smaller than four require an upstream,
64
+ scientifically justified decision rather than silent repair;
65
+ - users with dependent correlations must use a method outside the current
66
+ univariate scope.
@@ -19,7 +19,7 @@ print(ma.__version__)
19
19
  A provisional citation can use:
20
20
 
21
21
  ```text
22
- PyMetaAnalysis contributors. PyMetaAnalysis (version <version>):
22
+ Zhaobo Ding. PyMetaAnalysis (version <version>):
23
23
  a pandas-first meta-analysis library for Python.
24
24
  https://github.com/ZhaoboDing/PyMetaAnalysis
25
25
  ```
@@ -120,8 +120,9 @@ schema, strict JSON behavior, and subgroup reports.
120
120
  ## 6. Understand model and display scales
121
121
 
122
122
  Generic effects, MD, SMD, and RD use the identity scale. OR and RR are modeled
123
- on a log scale, so their audit-friendly numeric attributes remain logarithmic.
124
- Use display properties for ratios:
123
+ on a log scale, while `ZCOR` is modeled on Fisher's z scale. Their audit-
124
+ friendly numeric attributes remain on those model scales. Use display
125
+ properties for ratios and back-transformed correlations:
125
126
 
126
127
  ```python
127
128
  result.display_estimate
@@ -130,7 +131,8 @@ result.display_prediction_interval
130
131
  ```
131
132
 
132
133
  The [binary-outcome guide](guides/binary-outcomes.md) includes a complete ratio
133
- example.
134
+ example; the [correlation guide](guides/correlation-outcomes.md) explains the
135
+ Fisher transformation.
134
136
 
135
137
  ## 7. Check sensitivity
136
138
 
@@ -0,0 +1,150 @@
1
+ # Correlation outcomes
2
+
3
+ Use `meta_correlation()` when each independent study contributes a Pearson
4
+ correlation and its sample size. The first implementation deliberately
5
+ supports only Fisher's z (`measure="ZCOR"`), the conventional
6
+ variance-stabilizing route implemented by R `metafor::escalc(measure="ZCOR")`
7
+ and `meta::metacor(sm="ZCOR")`.
8
+
9
+ ## DataFrame input
10
+
11
+ ```python
12
+ import pandas as pd
13
+ import meta_analyze as ma
14
+
15
+ studies = pd.DataFrame(
16
+ {
17
+ "r": [0.18, 0.42, -0.05, 0.31, 0.27],
18
+ "sample_size": [84, 120, 63, 95, 150],
19
+ },
20
+ index=["Study A", "Study B", "Study C", "Study D", "Study E"],
21
+ )
22
+
23
+ result = ma.meta_correlation(
24
+ studies,
25
+ correlation="r",
26
+ n="sample_size",
27
+ model="random",
28
+ tau2_method="REML",
29
+ )
30
+ ```
31
+
32
+ Omitting `study=` uses the DataFrame index. Lists, NumPy arrays, and pandas
33
+ Series are also accepted directly.
34
+
35
+ ## Model and display scales
36
+
37
+ Each study is transformed before fitting:
38
+
39
+ ```text
40
+ z_i = atanh(r_i)
41
+ variance_i = 1 / (n_i - 3)
42
+ ```
43
+
44
+ The model, confidence interval, prediction interval, tau-squared, and
45
+ heterogeneity calculations remain on Fisher's z scale. This is explicit in
46
+ the result:
47
+
48
+ ```python
49
+ result.effect_scale # "fisher_z"
50
+ result.estimate # pooled Fisher's z
51
+ result.ci # Fisher's z confidence interval
52
+ ```
53
+
54
+ Use display properties for back-transformed correlations:
55
+
56
+ ```python
57
+ result.display_scale # "tanh"
58
+ result.display_estimate
59
+ result.display_ci
60
+ result.display_prediction_interval
61
+ ```
62
+
63
+ The pooled correlation is `tanh(pooled_z)`. It is not a direct weighted
64
+ average of the raw correlations.
65
+
66
+ ## Input boundaries
67
+
68
+ Included rows must satisfy:
69
+
70
+ - a finite correlation strictly between -1 and 1;
71
+ - a whole-number sample size of at least 4.
72
+
73
+ Values at -1 or 1 would produce an infinite Fisher's z. A sample size no
74
+ larger than 3 would make `1 / (n - 3)` non-positive or undefined. Both cases
75
+ raise `InvalidStudyDataError` instead of being clipped or corrected.
76
+
77
+ With `missing="drop"`, missing correlation or sample-size rows remain in
78
+ `study_results` as explicit exclusions. Invalid non-missing values still
79
+ raise an error.
80
+
81
+ ## Models and uncertainty
82
+
83
+ The default is a random-effects inverse-variance model with REML. Common-
84
+ effect pooling and the same random-effects choices as the generic API are
85
+ available:
86
+
87
+ ```python
88
+ common = ma.meta_correlation(
89
+ studies,
90
+ correlation="r",
91
+ n="sample_size",
92
+ model="common",
93
+ )
94
+
95
+ hk = ma.meta_correlation(
96
+ studies,
97
+ correlation="r",
98
+ n="sample_size",
99
+ model="random",
100
+ tau2_method="PM",
101
+ ci_method="hartung_knapp_adhoc",
102
+ )
103
+ ```
104
+
105
+ `tau2_method` supports REML, PM, and DL for random effects. Normal,
106
+ Hartung-Knapp, prediction-interval, and Q-profile behavior follows the shared
107
+ inverse-variance implementation and remains on the z scale until displayed.
108
+
109
+ ## Subgroups, sensitivity, reports, and plots
110
+
111
+ The standard workflows are available without reconstructing effects by hand:
112
+
113
+ ```python
114
+ studies = studies.assign(
115
+ population=["adult", "adult", "adult", "youth", "youth"],
116
+ publication_year=[2001, 2004, 2008, 2011, 2015],
117
+ )
118
+
119
+ subgroups = ma.meta_correlation(
120
+ studies,
121
+ correlation="r",
122
+ n="sample_size",
123
+ subgroup="population",
124
+ )
125
+
126
+ leave_one_out = result.leave_one_out().to_dataframe()
127
+ cumulative = result.cumulative(order="publication_year").to_dataframe()
128
+ methods = result.method_details()
129
+ report = result.report().to_dict()
130
+ forest_ax = result.forest()
131
+ funnel_ax = result.funnel()
132
+ ```
133
+
134
+ Forest and funnel x-coordinates are back-transformed correlations on a linear
135
+ axis by default. Funnel standard errors remain model-scale Fisher's z standard
136
+ errors.
137
+
138
+ ## Independence boundary
139
+
140
+ This API assumes one independent effect per study. Repeated outcomes,
141
+ different variable pairs, or multiple time points from the same participants
142
+ are statistically dependent even if their rows have different labels.
143
+ PyMetaAnalysis does not currently estimate the covariance matrix or fit a
144
+ multilevel/multivariate model for those data. Select one prespecified effect,
145
+ combine effects using an appropriate external method, or use software that
146
+ models dependence explicitly.
147
+
148
+ The first release also does not support raw-correlation pooling (`COR`),
149
+ partial correlations, rank correlations, or reliability corrections. Passing
150
+ `measure="COR"` raises `UnsupportedMethodError`.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Every analysis entry point accepts either DataFrame column names or
4
4
  one-dimensional array-like values. This page describes rules shared by the
5
- generic, binary, and continuous APIs.
5
+ generic, binary, continuous, and correlation APIs.
6
6
 
7
7
  ## DataFrame columns
8
8
 
@@ -105,8 +105,9 @@ silently assigning or dropping them would change the subgroup definition.
105
105
  | Generic | finite effect; finite, strictly positive sampling variance large enough for a finite float64 inverse weight |
106
106
  | Binary | integer event counts and totals; positive totals; `0 <= events <= total` |
107
107
  | Continuous | finite means/SDs; non-negative SDs; integer group sizes of at least 2 |
108
+ | Correlation | finite `-1 < correlation < 1`; integer sample size `n >= 4` |
108
109
 
109
- Binary and continuous APIs preserve their raw input columns in
110
+ Binary, continuous, and correlation APIs preserve their raw input columns in
110
111
  `study_results`. Derived effects, variances, correction indicators, and
111
112
  weights appear alongside them.
112
113
 
@@ -67,10 +67,11 @@ 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
+ Categorical matching keeps booleans distinct from numeric levels. Python and
71
+ NumPy integers are equivalent, and integer-valued floats such as `1.0` match
72
+ the corresponding integer level `1`. This accommodates pandas integer columns
73
+ that become floating point after introducing a missing value. Non-integer
74
+ floats such as `1.5` do not match integer levels.
74
75
 
75
76
  Formula parsing, automatic interactions, splines, and polynomial terms are not
76
77
  implemented. Construct those columns explicitly before fitting when they are
@@ -23,6 +23,7 @@ The high-level APIs intentionally have outcome-specific defaults:
23
23
  | --- | --- |
24
24
  | `meta_analysis()` | random-effects inverse variance, REML |
25
25
  | `meta_continuous()` | random-effects inverse variance, REML |
26
+ | `meta_correlation()` | random-effects inverse variance, REML |
26
27
  | `meta_binary()` | common-effect Mantel-Haenszel |
27
28
  | `meta_regression()` | mixed-effects inverse variance, REML |
28
29
 
@@ -40,6 +41,7 @@ protocols.
40
41
  | Binary RR/RD, common effect | Mantel-Haenszel or inverse variance |
41
42
  | Binary OR/RR/RD, random effects | Inverse variance |
42
43
  | Continuous MD/SMD, common or random | Inverse variance |
44
+ | Fisher's z correlation, common or random | Inverse variance |
43
45
 
44
46
  Mantel-Haenszel and inverse variance are different estimators, not aliases.
45
47
  PyMetaAnalysis does not extrapolate its common-effect Mantel-Haenszel weights
@@ -50,7 +50,9 @@ fig.tight_layout()
50
50
  | `log_scale` | Override the default logarithmic ratio axis |
51
51
 
52
52
  OR and RR are modeled on a log scale but displayed as ratios on a logarithmic
53
- axis by default. Other measures use an identity display scale and linear axis.
53
+ axis by default. `ZCOR` is modeled on Fisher's z scale, back-transformed to
54
+ correlations, and displayed on a linear axis. Other measures use an identity
55
+ display scale and linear axis.
54
56
  When overriding `log_scale=True`, all displayed effects and the null value must
55
57
  be strictly positive.
56
58
 
@@ -18,6 +18,7 @@ fixtures used by this project.
18
18
  | Binary 2x2 tables, Mantel-Haenszel | `meta_binary(..., method="MH")` | `rma.mh()` | `metabin(..., method="MH")` |
19
19
  | Binary 2x2 tables, Peto OR | `meta_binary(..., measure="OR", method="Peto")` | `rma.peto()` | `metabin(..., sm="OR", method="Peto")` |
20
20
  | Continuous group summaries | `meta_continuous()` | `escalc()` then `rma.uni()` | `metacont()` |
21
+ | Correlations and sample sizes | `meta_correlation()` | `escalc(measure="ZCOR")` then `rma.uni()` | `metacor(sm="ZCOR")` |
21
22
  | Subgroups | `subgroup=` on a high-level call | separate fits or a moderator model | `subgroup=` |
22
23
  | Leave-one-out | `result.leave_one_out()` | `leave1out()` for supported fits | `metainf()` |
23
24
  | Meta-regression influence | `regression.influence()` | `influence()`, `rstudent()`, `cooks.distance()`, `dfbetas()` | — |
@@ -42,6 +43,7 @@ provenance behavior consistent.
42
43
  | Control total | `n_control` | `n2i` | `n.c` |
43
44
  | Treatment mean/SD | `mean_treat`, `sd_treat` | `m1i`, `sd1i` | `mean.e`, `sd.e` |
44
45
  | Control mean/SD | `mean_control`, `sd_control` | `m2i`, `sd2i` | `mean.c`, `sd.c` |
46
+ | Correlation/sample size | `correlation`, `n` | `ri`, `ni` | `cor`, `n` |
45
47
 
46
48
  PyMetaAnalysis accepts DataFrame column names or aligned one-dimensional
47
49
  array-like values. When `study=` is omitted for a DataFrame, its index supplies
@@ -56,11 +58,14 @@ the display labels.
56
58
  | `"RD"` | Risk difference | `"RD"` | `"RD"` | identity |
57
59
  | `"MD"` | Mean difference | `"MD"` | `"MD"` | identity |
58
60
  | `"SMD"` | Exact-corrected Hedges' g | `"SMD"` with the documented correction | `"SMD"` with exact Hedges correction | identity |
61
+ | `"ZCOR"` | Fisher's z-transformed correlation | `"ZCOR"` | `"ZCOR"` | Fisher's z |
59
62
 
60
- For OR and RR, `estimate` and `ci` remain on the log model scale.
61
- `display_estimate` and `display_ci` provide exponentiated ratios. This is
62
- similar to choosing transformed or untransformed printing in R, but both scales
63
- remain explicit attributes in Python.
63
+ For OR and RR, `estimate` and `ci` remain on the log model scale;
64
+ `display_estimate` and `display_ci` provide exponentiated ratios. For `ZCOR`,
65
+ the model attributes remain Fisher's z values and the display attributes apply
66
+ `tanh` to return correlations. This is similar to choosing transformed or
67
+ untransformed printing in R, but both scales remain explicit attributes in
68
+ Python.
64
69
 
65
70
  ## Models, pooling, and heterogeneity
66
71
 
@@ -68,7 +73,7 @@ remain explicit attributes in Python.
68
73
  | --- | --- | --- | --- |
69
74
  | `model="common"` | `rma.uni(..., method="EE")` | `common=TRUE, random=FALSE` | Inverse-variance common-effect fit |
70
75
  | `model="random"` | random-effects `rma.uni()` | `random=TRUE` | Requires a tau-squared policy |
71
- | `method="IV"` | inverse-variance weighting | `method="Inverse"` | Binary API only; generic and continuous fits are IV |
76
+ | `method="IV"` | inverse-variance weighting | `method="Inverse"` | Binary API only; generic, continuous, and correlation fits are IV |
72
77
  | `method="MH"` | `rma.mh()` | `method="MH"` | Common-effect OR/RR/RD |
73
78
  | `method="Peto"` | `rma.peto()` | `method="Peto"` | Common-effect OR; raw pooling tables and O-minus-E heterogeneity |
74
79
  | `tau2_method=None` (resolved as `"REML"`) | `method="REML"` | `method.tau="REML"` | PyMetaAnalysis random-effects default |
@@ -217,3 +222,4 @@ as numerically equivalent.
217
222
  - [`meta::metagen`](https://search.r-project.org/CRAN/refmans/meta/html/metagen.html)
218
223
  - [`meta::metabin`](https://search.r-project.org/CRAN/refmans/meta/html/metabin.html)
219
224
  - [`meta::metacont`](https://search.r-project.org/CRAN/refmans/meta/html/metacont.html)
225
+ - [`meta::metacor`](https://search.r-project.org/CRAN/refmans/meta/html/metacor.html)
@@ -164,8 +164,8 @@ Every refit reuses the original result's:
164
164
  - tau-squared and confidence-interval methods;
165
165
  - confidence level, absolute tolerance, and iteration limit;
166
166
  - missing-data policy;
167
- - binary continuity corrections and RD zero-variance policy, or the continuous
168
- effect-size convention.
167
+ - binary continuity corrections and RD zero-variance policy, the continuous
168
+ effect-size convention, or the Fisher's z correlation convention.
169
169
 
170
170
  Meta-regression refits additionally reuse the intercept choice, inference
171
171
  method, moderator order, and complete explicit categorical level definitions.
@@ -76,20 +76,25 @@ default:
76
76
 
77
77
  ```python
78
78
  mh_continuity_correction = None
79
- mh_correction_scope = "only_zero_studies"
79
+ mh_correction_scope = None # resolves to "only_zero_studies" for MH
80
80
  ```
81
81
 
82
82
  `continuity_correction` still controls the study-level effects used for display
83
83
  and heterogeneity. It does not silently alter the pooled Mantel-Haenszel
84
84
  estimator. If an exact pooled estimator is undefined, choose an explicit
85
85
  positive `mh_continuity_correction` and report that decision.
86
+ Both MH-specific options are rejected when explicitly supplied to IV or Peto
87
+ pooling, where they would otherwise have no effect.
86
88
 
87
89
  For MH RD, `rd_zero_variance="exclude"` removes zero-variance boundary rows
88
90
  before every synthesis calculation. With the default `"correct"` policy, the
89
91
  raw table still enters the MH point estimate unless an explicit
90
92
  `mh_continuity_correction` is supplied. If the Sato-Greenland-Robins variance
91
93
  is non-positive, the uncorrected fit raises instead of silently changing the
92
- tables; a positive MH correction is an explicit protocol choice.
94
+ tables; a positive MH correction is an explicit protocol choice. Some other
95
+ implementations, including `metafor`, can report a degenerate zero or near-zero
96
+ standard error for such boundary data. PyMetaAnalysis rejects that result by
97
+ the policy recorded in ADR 0005.
93
98
 
94
99
  ## Peto pooling always uses raw tables
95
100
 
@@ -103,6 +108,10 @@ pooling-correction option.
103
108
  Double-zero and double-all studies have zero Peto information and are excluded
104
109
  before pooling, Q, I-squared, H-squared, and weights. The result retains the
105
110
  same structured exclusion reasons used for other relative-effect analyses.
111
+ This means `fit_peto()` expects already-filtered tables. It also differs from
112
+ the pooling side of `metafor::rma.peto()` under its default `drop00` handling,
113
+ which can retain zero-information rows when calculating `k` and Q degrees of
114
+ freedom even though those rows contribute no Peto information.
106
115
  Peto's lack of a single-zero pooling correction does not make it universally
107
116
  preferable: its rare-outcome, balanced-arm, and modest-effect assumptions must
108
117
  still be considered.
@@ -123,10 +132,10 @@ columns = [
123
132
  result.study_results[columns]
124
133
  ```
125
134
 
126
- Resolved correction values and scopes also appear in
127
- `dict(result.method.options)`. RD analyses additionally record the resolved
128
- zero-variance policy and affected row IDs in provenance. This makes it possible
129
- to distinguish a corrected analysis from an exact or exclusion-based one after
130
- fitting.
135
+ Applicable resolved correction values and scopes also appear in
136
+ `dict(result.method.options)`. MH-specific keys are present only for MH fits.
137
+ RD analyses additionally record the resolved zero-variance policy and affected
138
+ row IDs in provenance. This makes it possible to distinguish a corrected
139
+ analysis from an exact or exclusion-based one after fitting.
131
140
  Peto analyses additionally record `peto_pooling_tables="raw"` and
132
141
  `peto_heterogeneity="O-minus-E"`.