PyMetaAnalysis 0.1.0__py3-none-any.whl → 0.2.1__py3-none-any.whl
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.
- meta_analyze/_version.py +1 -1
- meta_analyze/api.py +85 -12
- meta_analyze/data.py +66 -24
- meta_analyze/reporting.py +7 -0
- {pymetaanalysis-0.1.0.dist-info → pymetaanalysis-0.2.1.dist-info}/METADATA +31 -21
- {pymetaanalysis-0.1.0.dist-info → pymetaanalysis-0.2.1.dist-info}/RECORD +8 -8
- {pymetaanalysis-0.1.0.dist-info → pymetaanalysis-0.2.1.dist-info}/WHEEL +0 -0
- {pymetaanalysis-0.1.0.dist-info → pymetaanalysis-0.2.1.dist-info}/licenses/LICENSE +0 -0
meta_analyze/_version.py
CHANGED
meta_analyze/api.py
CHANGED
|
@@ -13,7 +13,11 @@ from .data import ColumnOrArray, MissingPolicy, normalize_studies
|
|
|
13
13
|
from .estimators import fit_inverse_variance
|
|
14
14
|
from .exceptions import InvalidStudyDataError, UnsupportedMethodError
|
|
15
15
|
from .heterogeneity import classical_heterogeneity, tau2_inconsistency
|
|
16
|
-
from .provenance import
|
|
16
|
+
from .provenance import (
|
|
17
|
+
TransformationRecord,
|
|
18
|
+
add_input_field,
|
|
19
|
+
build_analysis_provenance,
|
|
20
|
+
)
|
|
17
21
|
from .results import (
|
|
18
22
|
FitDiagnostics,
|
|
19
23
|
HeterogeneityResult,
|
|
@@ -65,7 +69,8 @@ def _fit_meta_analysis_single(
|
|
|
65
69
|
data: pd.DataFrame | None = None,
|
|
66
70
|
*,
|
|
67
71
|
effect: ColumnOrArray,
|
|
68
|
-
variance: ColumnOrArray,
|
|
72
|
+
variance: ColumnOrArray | None = None,
|
|
73
|
+
standard_error: ColumnOrArray | None = None,
|
|
69
74
|
study: ColumnOrArray | None = None,
|
|
70
75
|
model: str = "random",
|
|
71
76
|
tau2_method: str = "REML",
|
|
@@ -82,9 +87,14 @@ def _fit_meta_analysis_single(
|
|
|
82
87
|
data:
|
|
83
88
|
Optional pandas DataFrame. String-valued input arguments select columns
|
|
84
89
|
from this frame.
|
|
85
|
-
effect
|
|
90
|
+
effect:
|
|
86
91
|
A DataFrame column name or one-dimensional array-like containing study
|
|
87
|
-
effects
|
|
92
|
+
effects.
|
|
93
|
+
variance, standard_error:
|
|
94
|
+
Exactly one must be provided as a DataFrame column name or
|
|
95
|
+
one-dimensional array-like. Values must be finite and strictly
|
|
96
|
+
positive. Standard errors are squared internally to obtain sampling
|
|
97
|
+
variances.
|
|
88
98
|
study:
|
|
89
99
|
Optional study label column/array. DataFrame input defaults to its index;
|
|
90
100
|
array-only input defaults to integer row labels.
|
|
@@ -118,6 +128,7 @@ def _fit_meta_analysis_single(
|
|
|
118
128
|
data=data,
|
|
119
129
|
effect=effect,
|
|
120
130
|
variance=variance,
|
|
131
|
+
standard_error=standard_error,
|
|
121
132
|
study=study,
|
|
122
133
|
missing=missing,
|
|
123
134
|
)
|
|
@@ -190,12 +201,30 @@ def _fit_meta_analysis_single(
|
|
|
190
201
|
max_iter=max_iter,
|
|
191
202
|
options=(),
|
|
192
203
|
)
|
|
204
|
+
transformations: tuple[TransformationRecord, ...] = ()
|
|
205
|
+
if standard_error is not None:
|
|
206
|
+
uncertainty_input = ("standard_error", standard_error)
|
|
207
|
+
transformed_rows = tuple(
|
|
208
|
+
int(row) for row in np.flatnonzero(~pd.isna(studies.variance))
|
|
209
|
+
)
|
|
210
|
+
transformations = (
|
|
211
|
+
TransformationRecord(
|
|
212
|
+
name="standard_error_to_variance",
|
|
213
|
+
affected_rows=transformed_rows,
|
|
214
|
+
),
|
|
215
|
+
)
|
|
216
|
+
else:
|
|
217
|
+
if variance is None: # pragma: no cover - validated by normalize_studies
|
|
218
|
+
raise RuntimeError("variance input unexpectedly missing")
|
|
219
|
+
uncertainty_input = ("variance", variance)
|
|
220
|
+
|
|
193
221
|
provenance = build_analysis_provenance(
|
|
194
222
|
analysis_type="generic",
|
|
195
223
|
data=data,
|
|
196
|
-
inputs=(("effect", effect),
|
|
224
|
+
inputs=(("effect", effect), uncertainty_input),
|
|
197
225
|
study=study,
|
|
198
226
|
included=studies.included,
|
|
227
|
+
transformations=transformations,
|
|
199
228
|
)
|
|
200
229
|
|
|
201
230
|
return MetaAnalysisResult(
|
|
@@ -226,6 +255,26 @@ def meta_analysis(
|
|
|
226
255
|
*,
|
|
227
256
|
effect: ColumnOrArray,
|
|
228
257
|
variance: ColumnOrArray,
|
|
258
|
+
standard_error: None = None,
|
|
259
|
+
study: ColumnOrArray | None = None,
|
|
260
|
+
subgroup: None = None,
|
|
261
|
+
model: str = "random",
|
|
262
|
+
tau2_method: str = "REML",
|
|
263
|
+
ci_method: str = "normal",
|
|
264
|
+
confidence_level: float = 0.95,
|
|
265
|
+
missing: MissingPolicy = "raise",
|
|
266
|
+
atol: float = 1e-10,
|
|
267
|
+
max_iter: int = 1000,
|
|
268
|
+
) -> MetaAnalysisResult: ...
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@overload
|
|
272
|
+
def meta_analysis(
|
|
273
|
+
data: pd.DataFrame | None = None,
|
|
274
|
+
*,
|
|
275
|
+
effect: ColumnOrArray,
|
|
276
|
+
variance: None = None,
|
|
277
|
+
standard_error: ColumnOrArray,
|
|
229
278
|
study: ColumnOrArray | None = None,
|
|
230
279
|
subgroup: None = None,
|
|
231
280
|
model: str = "random",
|
|
@@ -244,6 +293,7 @@ def meta_analysis(
|
|
|
244
293
|
*,
|
|
245
294
|
effect: ColumnOrArray,
|
|
246
295
|
variance: ColumnOrArray,
|
|
296
|
+
standard_error: None = None,
|
|
247
297
|
study: ColumnOrArray | None = None,
|
|
248
298
|
subgroup: ColumnOrArray,
|
|
249
299
|
model: str = "random",
|
|
@@ -256,11 +306,31 @@ def meta_analysis(
|
|
|
256
306
|
) -> SubgroupMetaAnalysisResult: ...
|
|
257
307
|
|
|
258
308
|
|
|
309
|
+
@overload
|
|
259
310
|
def meta_analysis(
|
|
260
311
|
data: pd.DataFrame | None = None,
|
|
261
312
|
*,
|
|
262
313
|
effect: ColumnOrArray,
|
|
263
|
-
variance:
|
|
314
|
+
variance: None = None,
|
|
315
|
+
standard_error: ColumnOrArray,
|
|
316
|
+
study: ColumnOrArray | None = None,
|
|
317
|
+
subgroup: ColumnOrArray,
|
|
318
|
+
model: str = "random",
|
|
319
|
+
tau2_method: str = "REML",
|
|
320
|
+
ci_method: str = "normal",
|
|
321
|
+
confidence_level: float = 0.95,
|
|
322
|
+
missing: MissingPolicy = "raise",
|
|
323
|
+
atol: float = 1e-10,
|
|
324
|
+
max_iter: int = 1000,
|
|
325
|
+
) -> SubgroupMetaAnalysisResult: ...
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def meta_analysis(
|
|
329
|
+
data: pd.DataFrame | None = None,
|
|
330
|
+
*,
|
|
331
|
+
effect: ColumnOrArray,
|
|
332
|
+
variance: ColumnOrArray | None = None,
|
|
333
|
+
standard_error: ColumnOrArray | None = None,
|
|
264
334
|
study: ColumnOrArray | None = None,
|
|
265
335
|
subgroup: ColumnOrArray | None = None,
|
|
266
336
|
model: str = "random",
|
|
@@ -273,18 +343,21 @@ def meta_analysis(
|
|
|
273
343
|
) -> MetaAnalysisResult | SubgroupMetaAnalysisResult:
|
|
274
344
|
"""Fit a generic inverse-variance meta-analysis, optionally by subgroup.
|
|
275
345
|
|
|
276
|
-
``effect`` and
|
|
277
|
-
array-like values.
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
346
|
+
``effect`` and the selected uncertainty input accept DataFrame column names
|
|
347
|
+
or one-dimensional array-like values. Supply exactly one of ``variance`` or
|
|
348
|
+
``standard_error``; standard errors are squared internally. Uncertainty
|
|
349
|
+
values must be finite and strictly positive. The default is a REML
|
|
350
|
+
random-effects model with a normal confidence interval. ``subgroup``
|
|
351
|
+
returns :class:`SubgroupMetaAnalysisResult` when supplied; otherwise the
|
|
352
|
+
return value is :class:`MetaAnalysisResult`. Missing subgroup labels are
|
|
353
|
+
rejected explicitly.
|
|
282
354
|
"""
|
|
283
355
|
|
|
284
356
|
overall = _fit_meta_analysis_single(
|
|
285
357
|
data,
|
|
286
358
|
effect=effect,
|
|
287
359
|
variance=variance,
|
|
360
|
+
standard_error=standard_error,
|
|
288
361
|
study=study,
|
|
289
362
|
model=model,
|
|
290
363
|
tau2_method=tau2_method,
|
meta_analyze/data.py
CHANGED
|
@@ -70,6 +70,7 @@ def _study_labels(
|
|
|
70
70
|
*,
|
|
71
71
|
data: pd.DataFrame | None,
|
|
72
72
|
length: int,
|
|
73
|
+
uncertainty_label: str = "variance",
|
|
73
74
|
) -> NDArray[np.object_]:
|
|
74
75
|
if study is None:
|
|
75
76
|
labels: NDArray[Any]
|
|
@@ -82,17 +83,33 @@ def _study_labels(
|
|
|
82
83
|
|
|
83
84
|
if len(labels) != length:
|
|
84
85
|
raise InvalidStudyDataError(
|
|
85
|
-
f"study has length {len(labels)}, but effect and
|
|
86
|
-
f"length {length}."
|
|
86
|
+
f"study has length {len(labels)}, but effect and {uncertainty_label} "
|
|
87
|
+
f"have length {length}."
|
|
87
88
|
)
|
|
88
89
|
return np.asarray(labels, dtype=object)
|
|
89
90
|
|
|
90
91
|
|
|
92
|
+
def _select_uncertainty_input(
|
|
93
|
+
variance: ColumnOrArray | None,
|
|
94
|
+
standard_error: ColumnOrArray | None,
|
|
95
|
+
) -> tuple[ColumnOrArray, str, str]:
|
|
96
|
+
if (variance is None) == (standard_error is None):
|
|
97
|
+
raise InvalidStudyDataError(
|
|
98
|
+
"Exactly one of variance or standard_error must be provided."
|
|
99
|
+
)
|
|
100
|
+
if standard_error is not None:
|
|
101
|
+
return standard_error, "standard_error", "standard error"
|
|
102
|
+
if variance is None: # pragma: no cover - guarded by the exclusive check
|
|
103
|
+
raise RuntimeError("variance input unexpectedly missing")
|
|
104
|
+
return variance, "variance", "variance"
|
|
105
|
+
|
|
106
|
+
|
|
91
107
|
def normalize_studies(
|
|
92
108
|
*,
|
|
93
109
|
data: pd.DataFrame | None,
|
|
94
110
|
effect: ColumnOrArray,
|
|
95
|
-
variance: ColumnOrArray,
|
|
111
|
+
variance: ColumnOrArray | None,
|
|
112
|
+
standard_error: ColumnOrArray | None = None,
|
|
96
113
|
study: ColumnOrArray | None,
|
|
97
114
|
missing: MissingPolicy,
|
|
98
115
|
) -> NormalizedStudies:
|
|
@@ -103,12 +120,15 @@ def normalize_studies(
|
|
|
103
120
|
if missing not in {"raise", "drop"}:
|
|
104
121
|
raise InvalidStudyDataError("missing must be either 'raise' or 'drop'.")
|
|
105
122
|
|
|
123
|
+
uncertainty, uncertainty_name, uncertainty_label = _select_uncertainty_input(
|
|
124
|
+
variance, standard_error
|
|
125
|
+
)
|
|
106
126
|
raw_effect = _resolve_vector(effect, data=data, name="effect")
|
|
107
|
-
|
|
108
|
-
if len(raw_effect) != len(
|
|
127
|
+
raw_uncertainty = _resolve_vector(uncertainty, data=data, name=uncertainty_name)
|
|
128
|
+
if len(raw_effect) != len(raw_uncertainty):
|
|
109
129
|
raise InvalidStudyDataError(
|
|
110
|
-
"effect and
|
|
111
|
-
f"got {len(raw_effect)} and {len(
|
|
130
|
+
f"effect and {uncertainty_label} must have the same length; "
|
|
131
|
+
f"got {len(raw_effect)} and {len(raw_uncertainty)}."
|
|
112
132
|
)
|
|
113
133
|
if data is not None and len(data) != len(raw_effect):
|
|
114
134
|
raise InvalidStudyDataError(
|
|
@@ -116,55 +136,77 @@ def normalize_studies(
|
|
|
116
136
|
"DataFrame row."
|
|
117
137
|
)
|
|
118
138
|
|
|
119
|
-
labels = _study_labels(
|
|
139
|
+
labels = _study_labels(
|
|
140
|
+
study,
|
|
141
|
+
data=data,
|
|
142
|
+
length=len(raw_effect),
|
|
143
|
+
uncertainty_label=uncertainty_label,
|
|
144
|
+
)
|
|
120
145
|
|
|
121
146
|
try:
|
|
122
147
|
effect_values = np.asarray(raw_effect, dtype=np.float64)
|
|
123
|
-
|
|
148
|
+
uncertainty_values = np.asarray(raw_uncertainty, dtype=np.float64)
|
|
124
149
|
except (TypeError, ValueError) as error:
|
|
125
150
|
raise InvalidStudyDataError(
|
|
126
|
-
"effect and
|
|
151
|
+
f"effect and {uncertainty_label} must contain numeric values."
|
|
127
152
|
) from error
|
|
128
153
|
|
|
129
154
|
effect_missing = pd.isna(effect_values)
|
|
130
|
-
|
|
131
|
-
any_missing = effect_missing |
|
|
155
|
+
uncertainty_missing = pd.isna(uncertainty_values)
|
|
156
|
+
any_missing = effect_missing | uncertainty_missing
|
|
132
157
|
if np.any(any_missing) and missing == "raise":
|
|
133
158
|
rows = np.flatnonzero(any_missing).tolist()
|
|
134
159
|
raise InvalidStudyDataError(
|
|
135
|
-
f"Missing effect or
|
|
160
|
+
f"Missing effect or {uncertainty_label} values at row positions {rows}; "
|
|
136
161
|
"use missing='drop' to exclude them explicitly."
|
|
137
162
|
)
|
|
138
163
|
|
|
139
164
|
finite_effect = np.isfinite(effect_values) | effect_missing
|
|
140
|
-
|
|
165
|
+
finite_uncertainty = np.isfinite(uncertainty_values) | uncertainty_missing
|
|
141
166
|
if not np.all(finite_effect):
|
|
142
167
|
rows = np.flatnonzero(~finite_effect).tolist()
|
|
143
168
|
raise InvalidStudyDataError(
|
|
144
169
|
f"Effect values must be finite; invalid rows: {rows}."
|
|
145
170
|
)
|
|
146
|
-
if not np.all(
|
|
147
|
-
rows = np.flatnonzero(~
|
|
171
|
+
if not np.all(finite_uncertainty):
|
|
172
|
+
rows = np.flatnonzero(~finite_uncertainty).tolist()
|
|
148
173
|
raise InvalidStudyDataError(
|
|
149
|
-
f"
|
|
174
|
+
f"{uncertainty_label.capitalize()} values must be finite; "
|
|
175
|
+
f"invalid rows: {rows}."
|
|
150
176
|
)
|
|
151
177
|
|
|
152
|
-
|
|
153
|
-
if np.any(
|
|
154
|
-
rows = np.flatnonzero(
|
|
178
|
+
nonpositive_uncertainty = (~uncertainty_missing) & (uncertainty_values <= 0.0)
|
|
179
|
+
if np.any(nonpositive_uncertainty):
|
|
180
|
+
rows = np.flatnonzero(nonpositive_uncertainty).tolist()
|
|
155
181
|
raise InvalidStudyDataError(
|
|
156
|
-
f"Sampling
|
|
182
|
+
f"Sampling {uncertainty_label}s must be strictly positive; "
|
|
183
|
+
f"invalid rows: {rows}."
|
|
157
184
|
)
|
|
158
185
|
|
|
186
|
+
if uncertainty_name == "standard_error":
|
|
187
|
+
with np.errstate(over="ignore", under="ignore", invalid="ignore"):
|
|
188
|
+
variance_values = np.square(uncertainty_values)
|
|
189
|
+
invalid_variance = (~uncertainty_missing) & (
|
|
190
|
+
(~np.isfinite(variance_values)) | (variance_values <= 0.0)
|
|
191
|
+
)
|
|
192
|
+
if np.any(invalid_variance):
|
|
193
|
+
rows = np.flatnonzero(invalid_variance).tolist()
|
|
194
|
+
raise InvalidStudyDataError(
|
|
195
|
+
"Standard errors must produce finite, strictly positive sampling "
|
|
196
|
+
f"variances after squaring; invalid rows: {rows}."
|
|
197
|
+
)
|
|
198
|
+
else:
|
|
199
|
+
variance_values = uncertainty_values
|
|
200
|
+
|
|
159
201
|
included = ~any_missing
|
|
160
202
|
reasons = np.full(len(effect_values), None, dtype=object)
|
|
161
203
|
for index in np.flatnonzero(any_missing):
|
|
162
|
-
if effect_missing[index] and
|
|
163
|
-
reasons[index] = "missing effect and
|
|
204
|
+
if effect_missing[index] and uncertainty_missing[index]:
|
|
205
|
+
reasons[index] = f"missing effect and {uncertainty_label}"
|
|
164
206
|
elif effect_missing[index]:
|
|
165
207
|
reasons[index] = "missing effect"
|
|
166
208
|
else:
|
|
167
|
-
reasons[index] = "missing
|
|
209
|
+
reasons[index] = f"missing {uncertainty_label}"
|
|
168
210
|
|
|
169
211
|
if not np.any(included):
|
|
170
212
|
raise InvalidStudyDataError(
|
meta_analyze/reporting.py
CHANGED
|
@@ -105,6 +105,13 @@ def method_details(result: MetaAnalysisResult) -> str:
|
|
|
105
105
|
f"using {_measure_description(result.measure)}, pooled with {pooling}."
|
|
106
106
|
]
|
|
107
107
|
|
|
108
|
+
standard_error_rows = _transformation_rows(result, "standard_error_to_variance")
|
|
109
|
+
if standard_error_rows:
|
|
110
|
+
sentences.append(
|
|
111
|
+
"Supplied standard errors were squared to obtain sampling "
|
|
112
|
+
f"variances for {len(standard_error_rows)} row(s)."
|
|
113
|
+
)
|
|
114
|
+
|
|
108
115
|
if result.model == "random":
|
|
109
116
|
sentences.append(
|
|
110
117
|
"Between-study variance was estimated with "
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyMetaAnalysis
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
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
|
|
7
7
|
Project-URL: Issues, https://github.com/ZhaoboDing/PyMetaAnalysis/issues
|
|
8
8
|
Project-URL: Changelog, https://github.com/ZhaoboDing/PyMetaAnalysis/blob/main/CHANGELOG.md
|
|
9
|
-
Author:
|
|
9
|
+
Author-email: Zhaobo Ding <ding.zb@yahoo.com>
|
|
10
10
|
Maintainer-email: Zhaobo Ding <ding.zb@yahoo.com>
|
|
11
11
|
License-Expression: MIT
|
|
12
12
|
License-File: LICENSE
|
|
@@ -51,7 +51,7 @@ Description-Content-Type: text/markdown
|
|
|
51
51
|
|
|
52
52
|
[](https://github.com/ZhaoboDing/PyMetaAnalysis/actions/workflows/ci.yml)
|
|
53
53
|
[](https://zhaoboding.github.io/PyMetaAnalysis/)
|
|
54
|
-
[](LICENSE)
|
|
54
|
+
[](https://github.com/ZhaoboDing/PyMetaAnalysis/blob/main/LICENSE)
|
|
55
55
|
|
|
56
56
|
PyMetaAnalysis is an early-stage, pandas-first Python library for conventional
|
|
57
57
|
study-level meta-analysis. It accepts DataFrames, NumPy arrays, and ordinary
|
|
@@ -115,7 +115,7 @@ subgroup differences.
|
|
|
115
115
|
|
|
116
116
|
| Input | Effects | Pooling/models |
|
|
117
117
|
| --- | --- | --- |
|
|
118
|
-
| Effect + sampling variance | Generic | Common/random inverse variance |
|
|
118
|
+
| Effect + sampling variance or standard error | Generic | Common/random inverse variance |
|
|
119
119
|
| Two-group events + totals | OR, RR, RD | Common MH OR/RR; common/random IV |
|
|
120
120
|
| Two-group means + SDs + sizes | MD, Hedges' g | Common/random inverse variance |
|
|
121
121
|
|
|
@@ -124,6 +124,10 @@ and DerSimonian-Laird tau-squared estimators. Mean confidence intervals support
|
|
|
124
124
|
the normal default plus unmodified and safeguarded Hartung-Knapp variants.
|
|
125
125
|
Eligible random-effects fits include an HTS prediction interval.
|
|
126
126
|
|
|
127
|
+
Generic analyses accept exactly one of `variance=` or `standard_error=`.
|
|
128
|
+
Standard errors are squared internally and the conversion is recorded in the
|
|
129
|
+
result provenance.
|
|
130
|
+
|
|
127
131
|
Sparse binary behavior is explicit: study-level and Mantel-Haenszel continuity
|
|
128
132
|
corrections are separate, relative-effect double-zero/double-all rows remain
|
|
129
133
|
visible as exclusions, and RD exposes
|
|
@@ -174,18 +178,18 @@ are descriptive small-study-effect diagnostics, not proof of publication bias.
|
|
|
174
178
|
The complete documentation is published at
|
|
175
179
|
[zhaoboding.github.io/PyMetaAnalysis](https://zhaoboding.github.io/PyMetaAnalysis/).
|
|
176
180
|
|
|
177
|
-
- [Installation](
|
|
178
|
-
- [Getting started](
|
|
179
|
-
- [Input data and row decisions](
|
|
180
|
-
- [Generic](
|
|
181
|
-
- [Choosing methods](
|
|
182
|
-
- [Sensitivity analysis](
|
|
183
|
-
- [Public API](
|
|
184
|
-
- [Validation strategy](
|
|
185
|
-
- [Citation guidance](
|
|
186
|
-
- [R `meta`/`metafor` mapping](
|
|
187
|
-
|
|
188
|
-
An executable [end-to-end notebook](examples/quickstart.ipynb) uses synthetic
|
|
181
|
+
- [Installation](https://zhaoboding.github.io/PyMetaAnalysis/installation/)
|
|
182
|
+
- [Getting started](https://zhaoboding.github.io/PyMetaAnalysis/getting-started/)
|
|
183
|
+
- [Input data and row decisions](https://zhaoboding.github.io/PyMetaAnalysis/guides/input-data/)
|
|
184
|
+
- [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
|
|
185
|
+
- [Choosing methods](https://zhaoboding.github.io/PyMetaAnalysis/guides/method-selection/) and [statistical formulas](https://zhaoboding.github.io/PyMetaAnalysis/methods/statistical-methods/)
|
|
186
|
+
- [Sensitivity analysis](https://zhaoboding.github.io/PyMetaAnalysis/guides/sensitivity-analysis/) and [plotting](https://zhaoboding.github.io/PyMetaAnalysis/guides/plotting/)
|
|
187
|
+
- [Public API](https://zhaoboding.github.io/PyMetaAnalysis/reference/api/), [result objects](https://zhaoboding.github.io/PyMetaAnalysis/reference/results/), and [report schema](https://zhaoboding.github.io/PyMetaAnalysis/reference/report-schema/)
|
|
188
|
+
- [Validation strategy](https://zhaoboding.github.io/PyMetaAnalysis/validation/) and [scope/limitations](https://zhaoboding.github.io/PyMetaAnalysis/limitations/)
|
|
189
|
+
- [Citation guidance](https://zhaoboding.github.io/PyMetaAnalysis/citation/)
|
|
190
|
+
- [R `meta`/`metafor` mapping](https://zhaoboding.github.io/PyMetaAnalysis/guides/r-interoperability/)
|
|
191
|
+
|
|
192
|
+
An executable [end-to-end notebook](https://github.com/ZhaoboDing/PyMetaAnalysis/blob/main/examples/quickstart.ipynb) uses synthetic
|
|
189
193
|
data to demonstrate analysis, provenance, reporting, sensitivity, and plotting.
|
|
190
194
|
|
|
191
195
|
Build the complete site locally with:
|
|
@@ -203,16 +207,22 @@ edge cases, and committed R `metafor` reference fixtures. CI covers Python
|
|
|
203
207
|
distribution builds.
|
|
204
208
|
|
|
205
209
|
This is independent cross-software validation, not a formal external
|
|
206
|
-
statistical audit. See
|
|
210
|
+
statistical audit. See
|
|
211
|
+
[validation](https://zhaoboding.github.io/PyMetaAnalysis/validation/) for exact
|
|
212
|
+
coverage.
|
|
207
213
|
|
|
208
214
|
## Contributing
|
|
209
215
|
|
|
210
|
-
See
|
|
211
|
-
[
|
|
216
|
+
See
|
|
217
|
+
[CONTRIBUTING.md](https://github.com/ZhaoboDing/PyMetaAnalysis/blob/main/CONTRIBUTING.md)
|
|
218
|
+
and the full
|
|
219
|
+
[development guide](https://zhaoboding.github.io/PyMetaAnalysis/development/).
|
|
220
|
+
Statistical changes require formula
|
|
212
221
|
documentation, boundary tests, and an independent comparison where available.
|
|
213
222
|
|
|
214
|
-
Security-sensitive reports should follow
|
|
223
|
+
Security-sensitive reports should follow
|
|
224
|
+
[SECURITY.md](https://github.com/ZhaoboDing/PyMetaAnalysis/blob/main/SECURITY.md).
|
|
215
225
|
|
|
216
226
|
## License
|
|
217
227
|
|
|
218
|
-
[MIT](LICENSE)
|
|
228
|
+
[MIT](https://github.com/ZhaoboDing/PyMetaAnalysis/blob/main/LICENSE)
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
meta_analyze/__init__.py,sha256=POInICUoVRrqR7s9sgJReHsbxBYNNlqagJDduHWC-3I,1602
|
|
2
|
-
meta_analyze/_version.py,sha256
|
|
3
|
-
meta_analyze/api.py,sha256=
|
|
2
|
+
meta_analyze/_version.py,sha256=bV7jlwuImq2OrGXG_x4vGXNic3lNrNcFEgpc6aKy37M,77
|
|
3
|
+
meta_analyze/api.py,sha256=pMMMzsKkiBc_3vvNxeqjZG3Rs3RJpyVDqr7BflhQaak,13080
|
|
4
4
|
meta_analyze/binary_api.py,sha256=kH_1zuAshRCJ_MjbbPt30CmMAPr49N2hASZmODnMaqM,18664
|
|
5
5
|
meta_analyze/config.py,sha256=IL8VX0xDRv18giwbk2m-P_XjBjOo_GsN_B-H4-C7s8E,831
|
|
6
6
|
meta_analyze/continuous_api.py,sha256=jAsVB9TqTDKGLw5MWCNGXxiBx5u9QVSeAR0fuN9WRYE,11457
|
|
7
|
-
meta_analyze/data.py,sha256=
|
|
7
|
+
meta_analyze/data.py,sha256=QCYNzEmjOgjVyFDIJrf4M4dTP-ZctPOOlUesx23KObw,7869
|
|
8
8
|
meta_analyze/exceptions.py,sha256=h4PPm8mcxSyUQax0YrqgIhy7GCF7wj2uVL4BWmwWImA,666
|
|
9
9
|
meta_analyze/heterogeneity.py,sha256=xQ2e5012AZG1YKuq2EDl4ccYgguC2Se5LJOT1WMHrVo,3308
|
|
10
10
|
meta_analyze/provenance.py,sha256=PrRubpJRn2muvJNun2juMKs-VV4QVT4T81BxvHNuKck,5858
|
|
11
11
|
meta_analyze/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
12
|
-
meta_analyze/reporting.py,sha256=
|
|
12
|
+
meta_analyze/reporting.py,sha256=tgkVVolIyWhXVdGBwm3bV32nPHJ8fppjRpOx0ucOv44,16244
|
|
13
13
|
meta_analyze/results.py,sha256=Jq9O0uwnmseQjYBNBSqtlXhh9U_D-th5b6nQHj3AsbQ,17048
|
|
14
14
|
meta_analyze/sensitivity.py,sha256=3p1Mu5AdeugYcLZtTV_2YEgfgmBJ_y1Bnbcgj33m5Mw,18584
|
|
15
15
|
meta_analyze/subgroups.py,sha256=CSevfbLfA79Z55RyLGKyjUSDl7TGladdHbrJXhKSzCY,5985
|
|
@@ -25,7 +25,7 @@ meta_analyze/plotting/_utils.py,sha256=3vg-0Wtjlf7LKpXkw2sNnkLNMAS83K_SwxXaneT74
|
|
|
25
25
|
meta_analyze/plotting/forest.py,sha256=NkqcJC7HLfDzl-fW2rNEeH2sozCVpZwsjFuPWFYVNKs,6088
|
|
26
26
|
meta_analyze/plotting/funnel.py,sha256=Mdy1O5uVw4OZF8PvXiDyG7AMANz7vkef9ru-D61_k1Q,5577
|
|
27
27
|
meta_analyze/plotting/subgroup_forest.py,sha256=hEIDtmme2javG6YgsHobAS5EK0lEoe07N0NweU9JDdg,8341
|
|
28
|
-
pymetaanalysis-0.1.
|
|
29
|
-
pymetaanalysis-0.1.
|
|
30
|
-
pymetaanalysis-0.1.
|
|
31
|
-
pymetaanalysis-0.1.
|
|
28
|
+
pymetaanalysis-0.2.1.dist-info/METADATA,sha256=BalGMtJG7h_7DplNp1lWzw35Y8go2b_EkasFPstVTMY,9040
|
|
29
|
+
pymetaanalysis-0.2.1.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
30
|
+
pymetaanalysis-0.2.1.dist-info/licenses/LICENSE,sha256=CBE_u7LfEteKb_1My8s9obP0YkjJVzcWlgHSDiqa9ts,1084
|
|
31
|
+
pymetaanalysis-0.2.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|