pyemmeans 0.1.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 (54) hide show
  1. pyemmeans-0.1.0/LICENSE +20 -0
  2. pyemmeans-0.1.0/PKG-INFO +72 -0
  3. pyemmeans-0.1.0/README.md +72 -0
  4. pyemmeans-0.1.0/README_PYPI.md +38 -0
  5. pyemmeans-0.1.0/pyemmeans/__init__.py +248 -0
  6. pyemmeans-0.1.0/pyemmeans/adapters/__init__.py +11 -0
  7. pyemmeans-0.1.0/pyemmeans/adapters/base.py +11 -0
  8. pyemmeans-0.1.0/pyemmeans/adapters/qdrg.py +65 -0
  9. pyemmeans-0.1.0/pyemmeans/adapters/statsmodels.py +165 -0
  10. pyemmeans-0.1.0/pyemmeans/contrast.py +255 -0
  11. pyemmeans-0.1.0/pyemmeans/contrast_families.py +266 -0
  12. pyemmeans-0.1.0/pyemmeans/cov_reduce.py +41 -0
  13. pyemmeans-0.1.0/pyemmeans/effect_size.py +97 -0
  14. pyemmeans-0.1.0/pyemmeans/emmeans.py +445 -0
  15. pyemmeans-0.1.0/pyemmeans/emmip.py +275 -0
  16. pyemmeans-0.1.0/pyemmeans/emtrends.py +132 -0
  17. pyemmeans-0.1.0/pyemmeans/extras.py +127 -0
  18. pyemmeans-0.1.0/pyemmeans/factors.py +233 -0
  19. pyemmeans-0.1.0/pyemmeans/interface.py +41 -0
  20. pyemmeans-0.1.0/pyemmeans/internal_compat.py +292 -0
  21. pyemmeans-0.1.0/pyemmeans/joint.py +247 -0
  22. pyemmeans-0.1.0/pyemmeans/methods.py +234 -0
  23. pyemmeans-0.1.0/pyemmeans/options.py +55 -0
  24. pyemmeans-0.1.0/pyemmeans/pairwise_display.py +171 -0
  25. pyemmeans-0.1.0/pyemmeans/qdrg.py +184 -0
  26. pyemmeans-0.1.0/pyemmeans/rbind.py +115 -0
  27. pyemmeans-0.1.0/pyemmeans/ref_grid.py +441 -0
  28. pyemmeans-0.1.0/pyemmeans/regrid.py +140 -0
  29. pyemmeans-0.1.0/pyemmeans/summary.py +408 -0
  30. pyemmeans-0.1.0/pyemmeans/transforms.py +166 -0
  31. pyemmeans-0.1.0/pyemmeans/types.py +123 -0
  32. pyemmeans-0.1.0/pyemmeans/utils.py +212 -0
  33. pyemmeans-0.1.0/pyemmeans/wrappers.py +71 -0
  34. pyemmeans-0.1.0/pyemmeans.egg-info/PKG-INFO +72 -0
  35. pyemmeans-0.1.0/pyemmeans.egg-info/SOURCES.txt +52 -0
  36. pyemmeans-0.1.0/pyemmeans.egg-info/dependency_links.txt +1 -0
  37. pyemmeans-0.1.0/pyemmeans.egg-info/requires.txt +11 -0
  38. pyemmeans-0.1.0/pyemmeans.egg-info/top_level.txt +1 -0
  39. pyemmeans-0.1.0/pyproject.toml +53 -0
  40. pyemmeans-0.1.0/setup.cfg +4 -0
  41. pyemmeans-0.1.0/tests/test_compat_exports.py +19 -0
  42. pyemmeans-0.1.0/tests/test_effect_size_and_internal_exports.py +69 -0
  43. pyemmeans-0.1.0/tests/test_emmeans_core.py +75 -0
  44. pyemmeans-0.1.0/tests/test_emmip.py +65 -0
  45. pyemmeans-0.1.0/tests/test_extensions.py +73 -0
  46. pyemmeans-0.1.0/tests/test_extras.py +61 -0
  47. pyemmeans-0.1.0/tests/test_factors_and_meanchg.py +49 -0
  48. pyemmeans-0.1.0/tests/test_namespace_export_coverage.py +21 -0
  49. pyemmeans-0.1.0/tests/test_pwpm_pwpp.py +42 -0
  50. pyemmeans-0.1.0/tests/test_r_parity_fixtures.py +200 -0
  51. pyemmeans-0.1.0/tests/test_refgrid_advanced.py +67 -0
  52. pyemmeans-0.1.0/tests/test_regularize_and_accessors.py +53 -0
  53. pyemmeans-0.1.0/tests/test_weights_edgecases.py +69 -0
  54. pyemmeans-0.1.0/tests/test_wrappers_and_covreduce.py +56 -0
@@ -0,0 +1,20 @@
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2026 pyemmeans contributors
5
+
6
+ This program is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ This program is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU General Public License for more details.
15
+
16
+ You should have received a copy of the GNU General Public License
17
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
18
+
19
+ SPDX-License-Identifier: GPL-3.0-or-later
20
+
@@ -0,0 +1,72 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyemmeans
3
+ Version: 0.1.0
4
+ Summary: Python rewrite of emmeans (estimated marginal means)
5
+ Author-email: Tuo Zhao <tourzhao@gatech.edu>
6
+ Maintainer-email: Tuo Zhao <tourzhao@gatech.edu>
7
+ License-Expression: GPL-3.0-or-later
8
+ Project-URL: Homepage, https://rvlenth.github.io/emmeans/
9
+ Keywords: estimated marginal means,least-squares means,statistics,post-hoc,linear models
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Scientific/Engineering
19
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: numpy>=1.24
24
+ Requires-Dist: scipy>=1.10
25
+ Requires-Dist: pandas>=2.0
26
+ Requires-Dist: patsy>=0.5
27
+ Requires-Dist: statsmodels>=0.14
28
+ Requires-Dist: matplotlib>=3.8
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=8; extra == "dev"
31
+ Requires-Dist: ruff>=0.5; extra == "dev"
32
+ Requires-Dist: mypy>=1.8; extra == "dev"
33
+ Dynamic: license-file
34
+
35
+ # pyemmeans
36
+
37
+ `pyemmeans` is a Python rewrite of core `emmeans` workflows for estimated marginal means (EMMs), contrasts, trends, and post-hoc inference.
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ pip install pyemmeans
43
+ ```
44
+
45
+ ## Quick Start
46
+
47
+ ```python
48
+ from pyemmeans import emmeans, pairs, ref_grid
49
+
50
+ # `fit` can be a supported statsmodels model result
51
+ rg = ref_grid(fit)
52
+ emm = emmeans(rg, "treatment")
53
+ print(emm.summary())
54
+ print(pairs(emm).summary(infer=(False, True)))
55
+ ```
56
+
57
+ ## Highlights
58
+
59
+ - Core EMM APIs: `ref_grid`, `emmeans`, `contrast`, `pairs`, `summary`, `confint`, `test`
60
+ - Trend and joint testing support: `emtrends`, `joint_tests`
61
+ - Multiple weight modes in `emmeans(..., weights=...)`
62
+ - Parity-oriented tests against R fixtures
63
+
64
+ ## Project Notes
65
+
66
+ This package is a parity-oriented Python implementation inspired by the R `emmeans` ecosystem.
67
+
68
+ ## Authorship and Attribution
69
+
70
+ - Python rewrite author: Tuo Zhao (`tourzhao@gatech.edu`)
71
+ - Original R `emmeans` package: developed by its R authors and contributors
72
+ - Full attribution details are included in `AUTHORS.md` in the source distribution.
@@ -0,0 +1,72 @@
1
+ # pyemmeans
2
+
3
+ Python rewrite of `emmeans` for estimated marginal means, contrasts, trends, and inference.
4
+
5
+ ## Documentation
6
+
7
+ - Python help docs (generated from all R Rd pages + vignettes): [`docs/README.md`](docs/README.md)
8
+ - Reference index: [`docs/reference-index.md`](docs/reference-index.md)
9
+ - Guide index: [`docs/guide-index.md`](docs/guide-index.md)
10
+
11
+ ## Authorship and attribution
12
+
13
+ - Python rewrite author: Tuo Zhao (`tourzhao@gatech.edu`)
14
+ - Original R `emmeans` package: developed by its R authors and contributors
15
+ - Full attribution details: [`AUTHORS.md`](AUTHORS.md)
16
+
17
+ ## Quick start
18
+
19
+ ```python
20
+ from pyemmeans import emmeans, ref_grid, pairs
21
+
22
+ rg = ref_grid(model_result)
23
+ emm = emmeans(rg, "treatment")
24
+ print(emm.summary())
25
+ print(pairs(emm).summary(infer=(False, True)))
26
+ ```
27
+
28
+ ## Implemented API (current)
29
+
30
+ - `ref_grid()`
31
+ - `emmeans()`, `lsmeans()`, `pmmeans()`
32
+ - `contrast()`, `pairs()`, `coef()`
33
+ - contrast families (`pairwise_emmc`, `tukey_emmc`, `trt_vs_ctrl_emmc`, ...)
34
+ - `summary()`, `confint()`, `test()`, `predict()`
35
+ - `regrid()`
36
+ - `eff_size()`
37
+ - `force_regular()`, `linfct()`
38
+ - `emtrends()`
39
+ - `joint_tests()`
40
+ - `pwpm()`, `pwpp()`
41
+ - `qdrg()`, `emmobj()`
42
+ - `rbind_emm_grid()`
43
+ - `emmip()`, `emmip_matplotlib()`
44
+ - factor utilities: `comb_facs()`, `split_fac()`, `add_grouping()`, `permute_levels()`
45
+ - wrapper aliases: `emm`, `lsmeans`, `lstrends`, `lsm`, `lsmobj`
46
+ `lsmip`
47
+ - covariate reducers: `make_meanint`, `meanint`, `make_symmint`, `symmint`
48
+
49
+ ## Current model support
50
+
51
+ - `statsmodels` formula-based OLS/GLM
52
+ - `qdrg` custom wrapper models
53
+
54
+ ## Performance check
55
+
56
+ - Run a reproducible local benchmark:
57
+ `python3 scripts/benchmark_emmeans.py`
58
+
59
+ ## PyPI release
60
+
61
+ - Release checklist:
62
+ `docs/PYPI_RELEASE.md`
63
+ - One-command release helper:
64
+ `scripts/release_pypi.sh`
65
+
66
+ ## Notes
67
+
68
+ - `nuisance` handling in `ref_grid()` is implemented with R-aligned behavior for `wt_nuis="equal"` and non-`equal` modes (`prop`, `outer`, `flat`, `cells` all follow proportional observed-frequency nuisance averaging, matching R internals), and nuisance factors that interact with other predictors are ignored.
69
+ - `counterfactuals` support is available in a baseline form (`actual_*` columns and `cf_grid` flow).
70
+ - `emmeans(..., weights=)` modes (`equal`, `proportional`, `outer`, `flat`, `cells`) plus `show.levels`, numeric vector weights, and matrix weights are covered by R parity fixtures and tests.
71
+ - The package is validated with smoke/integration tests in `tests/`.
72
+ - R-side baseline fixtures can be regenerated with `Rscript python-package/scripts/generate_r_fixtures.R`.
@@ -0,0 +1,38 @@
1
+ # pyemmeans
2
+
3
+ `pyemmeans` is a Python rewrite of core `emmeans` workflows for estimated marginal means (EMMs), contrasts, trends, and post-hoc inference.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install pyemmeans
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from pyemmeans import emmeans, pairs, ref_grid
15
+
16
+ # `fit` can be a supported statsmodels model result
17
+ rg = ref_grid(fit)
18
+ emm = emmeans(rg, "treatment")
19
+ print(emm.summary())
20
+ print(pairs(emm).summary(infer=(False, True)))
21
+ ```
22
+
23
+ ## Highlights
24
+
25
+ - Core EMM APIs: `ref_grid`, `emmeans`, `contrast`, `pairs`, `summary`, `confint`, `test`
26
+ - Trend and joint testing support: `emtrends`, `joint_tests`
27
+ - Multiple weight modes in `emmeans(..., weights=...)`
28
+ - Parity-oriented tests against R fixtures
29
+
30
+ ## Project Notes
31
+
32
+ This package is a parity-oriented Python implementation inspired by the R `emmeans` ecosystem.
33
+
34
+ ## Authorship and Attribution
35
+
36
+ - Python rewrite author: Tuo Zhao (`tourzhao@gatech.edu`)
37
+ - Original R `emmeans` package: developed by its R authors and contributors
38
+ - Full attribution details are included in `AUTHORS.md` in the source distribution.
@@ -0,0 +1,248 @@
1
+ from __future__ import annotations
2
+
3
+ from .contrast import coef, contrast, pairs, weights
4
+ from .contrast_families import (
5
+ consec_emmc,
6
+ del_eff_emmc,
7
+ dunnett_emmc,
8
+ eff_emmc,
9
+ helmert_emmc,
10
+ identity_emmc,
11
+ mean_chg_emmc,
12
+ nrmlz_emmc,
13
+ opoly_emmc,
14
+ pairwise_emmc,
15
+ poly_emmc,
16
+ revpairwise_emmc,
17
+ trt_vs_ctrl1_emmc,
18
+ trt_vs_ctrl_emmc,
19
+ trt_vs_ctrlk_emmc,
20
+ tukey_emmc,
21
+ wtcon_emmc,
22
+ )
23
+ from .cov_reduce import inverse, make_meanint, make_symmint, meanint, symmint
24
+ from .emmip import emmip, emmip_ggplot, emmip_lattice, emmip_matplotlib
25
+ from .emmeans import emmeans, pmmeans
26
+ from .emtrends import emtrends
27
+ from .effect_size import eff_size
28
+ from .extras import (
29
+ as_emm_list,
30
+ as_glht,
31
+ as_mcmc_emm_grid,
32
+ as_mcmc_emm_list,
33
+ as_mcmc_list_emm_grid,
34
+ as_mcmc_list_emm_list,
35
+ emm_example,
36
+ hpd_summary,
37
+ mvcontrast,
38
+ mvregrid,
39
+ )
40
+ from .factors import add_grouping, add_submodels, comb_facs, permute_levels, split_fac
41
+ from .interface import emm_basis, recover_data
42
+ from .internal_compat import (
43
+ all_vars,
44
+ aovlist_dffun,
45
+ cmpMM,
46
+ combine_terms,
47
+ diag,
48
+ emm_register,
49
+ emm_vignette,
50
+ get_excl,
51
+ get_offset,
52
+ hurdle_support,
53
+ my_vcov,
54
+ num_key,
55
+ std_link_labels,
56
+ zi_support,
57
+ )
58
+ from .joint import joint_tests, test_emm_grid
59
+ from .methods import as_emm_grid, emm_defaults, force_regular, linfct, update_emm_grid, vcov_emm_grid
60
+ from .options import emm_options, get_emm_option, with_emm_options
61
+ from .qdrg import emmobj, qdrg
62
+ from .pairwise_display import pwpm, pwpp
63
+ from .rbind import add_emm_grid, rbind_emm_grid
64
+ from .ref_grid import get_last_ref_grid, ref_grid
65
+ from .regrid import regrid
66
+ from .summary import confint_emm_grid, predict_emm_grid, summary_emm_grid
67
+ from .transforms import Link, make_link, make_tran
68
+ from .types import EmmBasisResult, EmmGrid, QDRGModel, RecoverDataResult
69
+ from .wrappers import emm, get_lsm_option, lsm, lsm_options, lsmobj, lsmeans, lsmip, lstrends
70
+
71
+ __version__ = "0.1.0"
72
+
73
+
74
+ def summary(object: EmmGrid, **kwargs):
75
+ return summary_emm_grid(object, **kwargs)
76
+
77
+
78
+ def confint(object: EmmGrid, **kwargs):
79
+ return confint_emm_grid(object, **kwargs)
80
+
81
+
82
+ def test(object: EmmGrid, **kwargs):
83
+ return test_emm_grid(object, **kwargs)
84
+
85
+
86
+ def predict(object: EmmGrid, **kwargs):
87
+ return predict_emm_grid(object, **kwargs)
88
+
89
+
90
+ __all__ = [
91
+ "EmmGrid",
92
+ "RecoverDataResult",
93
+ "EmmBasisResult",
94
+ "QDRGModel",
95
+ "ref_grid",
96
+ "get_last_ref_grid",
97
+ "emmeans",
98
+ "emm",
99
+ "lsmeans",
100
+ "lstrends",
101
+ "lsm",
102
+ "lsmip",
103
+ "lsmobj",
104
+ "emmip",
105
+ "emmip_ggplot",
106
+ "emmip_lattice",
107
+ "emmip_matplotlib",
108
+ "pmmeans",
109
+ "emtrends",
110
+ "contrast",
111
+ "pairs",
112
+ "coef",
113
+ "weights",
114
+ "pairwise_emmc",
115
+ "revpairwise_emmc",
116
+ "consec_emmc",
117
+ "tukey_emmc",
118
+ "trt_vs_ctrl_emmc",
119
+ "trt_vs_ctrl1_emmc",
120
+ "trt_vs_ctrlk_emmc",
121
+ "dunnett_emmc",
122
+ "eff_emmc",
123
+ "mean_chg_emmc",
124
+ "del_eff_emmc",
125
+ "identity_emmc",
126
+ "poly_emmc",
127
+ "opoly_emmc",
128
+ "helmert_emmc",
129
+ "nrmlz_emmc",
130
+ "wtcon_emmc",
131
+ "regrid",
132
+ "force_regular",
133
+ "summary",
134
+ "confint",
135
+ "test",
136
+ "predict",
137
+ "summary_emm_grid",
138
+ "confint_emm_grid",
139
+ "predict_emm_grid",
140
+ "test_emm_grid",
141
+ "joint_tests",
142
+ "eff_size",
143
+ "pwpm",
144
+ "pwpp",
145
+ "mvcontrast",
146
+ "mvregrid",
147
+ "comb_facs",
148
+ "split_fac",
149
+ "add_grouping",
150
+ "add_submodels",
151
+ "permute_levels",
152
+ "rbind_emm_grid",
153
+ "add_emm_grid",
154
+ "qdrg",
155
+ "emmobj",
156
+ "as_emm_list",
157
+ "as_glht",
158
+ "as_mcmc_emm_grid",
159
+ "as_mcmc_emm_list",
160
+ "as_mcmc_list_emm_grid",
161
+ "as_mcmc_list_emm_list",
162
+ "hpd_summary",
163
+ "emm_example",
164
+ "recover_data",
165
+ "emm_basis",
166
+ "all_vars",
167
+ "aovlist_dffun",
168
+ "cmpMM",
169
+ "combine_terms",
170
+ "diag",
171
+ "emm_register",
172
+ "emm_vignette",
173
+ "get_excl",
174
+ "get_offset",
175
+ "hurdle_support",
176
+ "my_vcov",
177
+ "num_key",
178
+ "std_link_labels",
179
+ "zi_support",
180
+ "vcov_emm_grid",
181
+ "linfct",
182
+ "as_emm_grid",
183
+ "update_emm_grid",
184
+ "emm_defaults",
185
+ "emm_options",
186
+ "get_emm_option",
187
+ "lsm_options",
188
+ "get_lsm_option",
189
+ "with_emm_options",
190
+ "make_meanint",
191
+ "meanint",
192
+ "make_symmint",
193
+ "symmint",
194
+ "inverse",
195
+ "Link",
196
+ "make_link",
197
+ "make_tran",
198
+ ]
199
+
200
+ # R-style compatibility aliases (dot names)
201
+ _DOT_ALIASES = {
202
+ ".all.vars": all_vars,
203
+ ".aovlist.dffun": aovlist_dffun,
204
+ ".cmpMM": cmpMM,
205
+ ".combine.terms": combine_terms,
206
+ ".diag": diag,
207
+ ".emm_register": emm_register,
208
+ ".emm_vignette": emm_vignette,
209
+ ".get.excl": get_excl,
210
+ ".get.offset": get_offset,
211
+ ".hurdle.support": hurdle_support,
212
+ ".my.vcov": my_vcov,
213
+ ".num.key": num_key,
214
+ ".std.link.labels": std_link_labels,
215
+ ".zi.support": zi_support,
216
+ "get.lsm.option": get_lsm_option,
217
+ "lsm.options": lsm_options,
218
+ "make.meanint": make_meanint,
219
+ "make.symmint": make_symmint,
220
+ "make.tran": make_tran,
221
+ "as.emmGrid": as_emm_grid,
222
+ "as.emm_list": as_emm_list,
223
+ "as.glht": as_glht,
224
+ "as.mcmc.emmGrid": as_mcmc_emm_grid,
225
+ "as.mcmc.emm_list": as_mcmc_emm_list,
226
+ "as.mcmc.list.emmGrid": as_mcmc_list_emm_grid,
227
+ "as.mcmc.list.emm_list": as_mcmc_list_emm_list,
228
+ "hpd.summary": hpd_summary,
229
+ "pairwise.emmc": pairwise_emmc,
230
+ "revpairwise.emmc": revpairwise_emmc,
231
+ "consec.emmc": consec_emmc,
232
+ "tukey.emmc": tukey_emmc,
233
+ "trt.vs.ctrl.emmc": trt_vs_ctrl_emmc,
234
+ "trt.vs.ctrl1.emmc": trt_vs_ctrl1_emmc,
235
+ "trt.vs.ctrlk.emmc": trt_vs_ctrlk_emmc,
236
+ "dunnett.emmc": dunnett_emmc,
237
+ "eff.emmc": eff_emmc,
238
+ "mean_chg.emmc": mean_chg_emmc,
239
+ "del.eff.emmc": del_eff_emmc,
240
+ "identity.emmc": identity_emmc,
241
+ "poly.emmc": poly_emmc,
242
+ "opoly.emmc": opoly_emmc,
243
+ "helmert.emmc": helmert_emmc,
244
+ "nrmlz.emmc": nrmlz_emmc,
245
+ "wtcon.emmc": wtcon_emmc,
246
+ }
247
+ for _k, _v in _DOT_ALIASES.items():
248
+ globals()[_k] = _v
@@ -0,0 +1,11 @@
1
+ from .base import EmmAdapter
2
+ from .qdrg import emm_basis_qdrg, recover_data_qdrg
3
+ from .statsmodels import emm_basis_statsmodels, recover_data_statsmodels
4
+
5
+ __all__ = [
6
+ "EmmAdapter",
7
+ "recover_data_qdrg",
8
+ "emm_basis_qdrg",
9
+ "recover_data_statsmodels",
10
+ "emm_basis_statsmodels",
11
+ ]
@@ -0,0 +1,11 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Protocol
4
+
5
+ from ..types import EmmBasisResult, RecoverDataResult
6
+
7
+
8
+ class EmmAdapter(Protocol):
9
+ def recover_data(self, model: Any, **kwargs: Any) -> RecoverDataResult: ...
10
+
11
+ def emm_basis(self, model: Any, trms: str, xlev: dict[str, list[Any]], grid, **kwargs: Any) -> EmmBasisResult: ...
@@ -0,0 +1,65 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ import numpy as np
6
+ import pandas as pd
7
+ from patsy import dmatrix
8
+
9
+ from ..transforms import make_link
10
+ from ..types import EmmBasisResult, QDRGModel, RecoverDataResult
11
+
12
+
13
+ def recover_data_qdrg(model: QDRGModel, **kwargs: Any) -> RecoverDataResult:
14
+ formula = model.formula
15
+ lhs, rhs = [x.strip() for x in formula.split("~", 1)]
16
+ predictors = [c for c in model.data.columns if c in rhs or c == lhs or True]
17
+ # keep all columns to allow transformed expressions and at= usage
18
+ return RecoverDataResult(
19
+ data=model.data.copy(),
20
+ terms=formula,
21
+ predictors=[c for c in model.data.columns if c != lhs],
22
+ responses=[lhs],
23
+ call=None,
24
+ misc={},
25
+ )
26
+
27
+
28
+ def emm_basis_qdrg(model: QDRGModel, trms: str, xlev: dict[str, list[Any]], grid: pd.DataFrame, **kwargs: Any) -> EmmBasisResult:
29
+ lhs, rhs = [x.strip() for x in trms.split("~", 1)]
30
+ Xdf = dmatrix(rhs, data=grid, return_type="dataframe")
31
+ X = np.asarray(Xdf, dtype=float)
32
+
33
+ coef = np.asarray(model.coef, dtype=float)
34
+ if coef.ndim > 1:
35
+ coef = coef.ravel()
36
+ p = X.shape[1]
37
+ if coef.size != p:
38
+ raise ValueError(f"Non-conforming coef length ({coef.size}) vs model matrix columns ({p})")
39
+
40
+ V = np.asarray(model.vcov, dtype=float)
41
+ if V.shape != (p, p):
42
+ raise ValueError(f"Non-conforming vcov shape {V.shape}; expected {(p, p)}")
43
+
44
+ def dffun(k: np.ndarray, dfargs: dict[str, Any]) -> float:
45
+ return float(dfargs["df"])
46
+
47
+ misc: dict[str, Any] = {}
48
+ if model.link:
49
+ misc["tran"] = make_link(model.link)
50
+ misc["inv_lbl"] = "response"
51
+
52
+ post_beta = None
53
+ if model.mcmc is not None:
54
+ post_beta = np.asarray(model.mcmc, dtype=float)
55
+
56
+ return EmmBasisResult(
57
+ X=X,
58
+ bhat=coef,
59
+ nbasis=np.array([[np.nan]]),
60
+ V=V,
61
+ dffun=dffun,
62
+ dfargs={"df": float(model.df)},
63
+ misc=misc,
64
+ post_beta=post_beta,
65
+ )