multivariate-probit 0.1.0__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.
- multivariate_probit/__init__.py +56 -0
- multivariate_probit/_corr.py +51 -0
- multivariate_probit/_mvn.py +155 -0
- multivariate_probit/ifm.py +177 -0
- multivariate_probit/inner.py +187 -0
- multivariate_probit/linear.py +118 -0
- multivariate_probit/model.py +365 -0
- multivariate_probit/results.py +72 -0
- multivariate_probit-0.1.0.dist-info/METADATA +175 -0
- multivariate_probit-0.1.0.dist-info/RECORD +12 -0
- multivariate_probit-0.1.0.dist-info/WHEEL +4 -0
- multivariate_probit-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: multivariate-probit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Multivariate probit models fitted by Inference Functions for Margins (IFM), with pluggable inner models.
|
|
5
|
+
Project-URL: Homepage, https://github.com/sign-of-fourier/multivariate-probit
|
|
6
|
+
Project-URL: Documentation, https://github.com/sign-of-fourier/multivariate-probit/blob/main/docs/ifm.md
|
|
7
|
+
Project-URL: Issues, https://github.com/sign-of-fourier/multivariate-probit/issues
|
|
8
|
+
Author: Mark Shipman
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2026 Mark Shipman
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Keywords: IFM,copula,correlated binary outcomes,multivariate probit,xgboost
|
|
32
|
+
Classifier: Development Status :: 3 - Alpha
|
|
33
|
+
Classifier: Intended Audience :: Science/Research
|
|
34
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
35
|
+
Classifier: Programming Language :: Python :: 3
|
|
36
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
37
|
+
Requires-Python: >=3.9
|
|
38
|
+
Requires-Dist: numpy>=1.22
|
|
39
|
+
Requires-Dist: scipy>=1.8
|
|
40
|
+
Provides-Extra: all
|
|
41
|
+
Requires-Dist: scikit-learn>=1.1; extra == 'all'
|
|
42
|
+
Requires-Dist: xgboost>=1.7; extra == 'all'
|
|
43
|
+
Provides-Extra: dev
|
|
44
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
45
|
+
Requires-Dist: scikit-learn>=1.1; extra == 'dev'
|
|
46
|
+
Requires-Dist: xgboost>=1.7; extra == 'dev'
|
|
47
|
+
Provides-Extra: sklearn
|
|
48
|
+
Requires-Dist: scikit-learn>=1.1; extra == 'sklearn'
|
|
49
|
+
Provides-Extra: xgboost
|
|
50
|
+
Requires-Dist: scikit-learn>=1.1; extra == 'xgboost'
|
|
51
|
+
Requires-Dist: xgboost>=1.7; extra == 'xgboost'
|
|
52
|
+
Description-Content-Type: text/markdown
|
|
53
|
+
|
|
54
|
+
# multivariate-probit
|
|
55
|
+
|
|
56
|
+
Multivariate probit models for correlated binary outcomes, with a pluggable
|
|
57
|
+
inner model.
|
|
58
|
+
|
|
59
|
+
## The model
|
|
60
|
+
|
|
61
|
+
Each outcome is a threshold on a latent Gaussian variable, and the outcomes are
|
|
62
|
+
tied together by the correlation of those latents:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
Y_j = 1[ η_j(x) + e_j > 0 ], e ~ N(0, Σ), j = 1 … d
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`η_j` is an arbitrary real-valued function of the features — linear by default,
|
|
69
|
+
or XGBoost, a random forest, or anything else that fits `(X, y)`. Σ is a
|
|
70
|
+
correlation matrix (unit diagonal) carrying the dependence between outcomes.
|
|
71
|
+
Marginally, `P(Y_j = 1 | x) = Φ(η_j(x))`.
|
|
72
|
+
|
|
73
|
+
Fitting is cross-fit two-stage IFM: every margin is fitted independently, then
|
|
74
|
+
Σ is estimated by maximum likelihood with those margins held fixed. That
|
|
75
|
+
separation is what lets the inner model be a black box. The algorithm, and the
|
|
76
|
+
alternatives that were tested and rejected, are in
|
|
77
|
+
**[docs/ifm.md](docs/ifm.md)**.
|
|
78
|
+
|
|
79
|
+
## Install
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install multivariate-probit # core: numpy + scipy
|
|
83
|
+
pip install multivariate-probit[xgboost] # adds the "xgboost" preset
|
|
84
|
+
pip install multivariate-probit[all] # every preset
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Quickstart
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from multivariate_probit import MultivariateProbit
|
|
91
|
+
|
|
92
|
+
model = MultivariateProbit(inner="linear").fit(X, Y) # Y is (n, d), 0/1
|
|
93
|
+
|
|
94
|
+
proba = model.predict_proba(X)
|
|
95
|
+
proba.marginal # P(Y_j = 1 | x), shape (n, d)
|
|
96
|
+
proba.joint([1, 0, 1]) # P(Y = pattern | x), shape (n,)
|
|
97
|
+
proba.all() # P(every outcome = 1 | x)
|
|
98
|
+
proba.any(outcomes=[0, 2]) # P(at least one of these | x)
|
|
99
|
+
|
|
100
|
+
model.correlation_ # the fitted Σ, shape (d, d)
|
|
101
|
+
model.transform(X) # latent scores η, shape (n, d)
|
|
102
|
+
model.sample(X, n_samples=100) # simulated outcome patterns
|
|
103
|
+
model.score(X, Y) # mean joint log-likelihood
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`predict_proba` returns an object that behaves like the marginal-probability
|
|
107
|
+
array (`np.asarray(proba)`, indexing, `.shape`) and additionally answers the
|
|
108
|
+
joint questions Σ was estimated for. Marginal predictions do not involve Σ at
|
|
109
|
+
all; everything joint does.
|
|
110
|
+
|
|
111
|
+
## Everything here is a squashing function over a latent index
|
|
112
|
+
|
|
113
|
+
The inner model never sees a probability, and never sees another outcome's
|
|
114
|
+
labels. It produces an unbounded score η_j(x) on (-∞, ∞); Φ is the only
|
|
115
|
+
squashing function applied to it. Any estimator that emits a real-valued score,
|
|
116
|
+
or a probability that can be pushed back through Φ⁻¹, is a legal margin.
|
|
117
|
+
|
|
118
|
+
That is the whole abstraction, and it is why the inner model is swappable
|
|
119
|
+
without touching the estimation code.
|
|
120
|
+
|
|
121
|
+
## Inner models
|
|
122
|
+
|
|
123
|
+
| Preset | Estimator | Requires |
|
|
124
|
+
| --- | --- | --- |
|
|
125
|
+
| `"linear"` (default), `"probit"` | native probit via IRLS | — |
|
|
126
|
+
| `"xgboost"`, `"xgb"` | `XGBClassifier`, tuned for calibration | `xgboost` |
|
|
127
|
+
| `"rf"`, `"random_forest"` | `RandomForestClassifier` | `scikit-learn` |
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
MultivariateProbit(inner="xgboost") # a preset
|
|
131
|
+
MultivariateProbit(inner=XGBClassifier(max_depth=4)) # any sklearn-shaped model
|
|
132
|
+
MultivariateProbit(inner=["linear", "xgboost", "linear"]) # one per outcome
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`available_inners()` lists the presets; `register_inner(name, factory)` adds
|
|
136
|
+
your own. See **[docs/api.md](docs/api.md)** for the inner-model contract.
|
|
137
|
+
|
|
138
|
+
## Two knobs that cost time
|
|
139
|
+
|
|
140
|
+
- **`dependence`** — `"joint"` (default) maximises the full d-variate
|
|
141
|
+
likelihood for Σ. `"pairwise"` maximises each pair's bivariate likelihood
|
|
142
|
+
instead: consistent, orders of magnitude cheaper, and the right choice once
|
|
143
|
+
you have more than a handful of outcomes.
|
|
144
|
+
- **`cv`** — `5` by default, cross-fitting the margins so Σ is never estimated
|
|
145
|
+
from in-sample predictions. This is not optional hygiene: in-sample margins
|
|
146
|
+
drive every fitted correlation to +1. `cv=None` skips it, which is defensible
|
|
147
|
+
for the linear default and reckless for anything that can overfit.
|
|
148
|
+
|
|
149
|
+
## Status
|
|
150
|
+
|
|
151
|
+
Alpha. The linear and XGBoost paths are covered by tests; the `rf` preset is
|
|
152
|
+
wired but untested. Standard errors are not computed — `correlation_` is a
|
|
153
|
+
point estimate. Known gaps are listed in
|
|
154
|
+
**[docs/limitations.md](docs/limitations.md)**.
|
|
155
|
+
|
|
156
|
+
## Documentation
|
|
157
|
+
|
|
158
|
+
- **[docs/ifm.md](docs/ifm.md)** — the estimation algorithm, and why IFM over
|
|
159
|
+
the alternatives
|
|
160
|
+
- **[docs/implementation.md](docs/implementation.md)** — what is hand-rolled,
|
|
161
|
+
what comes from SciPy, and why
|
|
162
|
+
- **[docs/api.md](docs/api.md)** — parameters, attributes, methods, extension
|
|
163
|
+
points
|
|
164
|
+
- **[docs/limitations.md](docs/limitations.md)** — known gaps and roadmap
|
|
165
|
+
|
|
166
|
+
## Development
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
pip install -e ".[dev]"
|
|
170
|
+
pytest
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## License
|
|
174
|
+
|
|
175
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
multivariate_probit/__init__.py,sha256=01q0z7P1z8sMRRxmlEFgdWCekyywt4a4XqUvByJM-zU,1623
|
|
2
|
+
multivariate_probit/_corr.py,sha256=z2XtcqZkbVR8FXTgCiSjgEvxuEb79L0t2nO7TIUm7bI,1623
|
|
3
|
+
multivariate_probit/_mvn.py,sha256=leDssaYIjNuAwlQltQpMdGK4MOeVUAS-lIQVxL1kz4Y,6070
|
|
4
|
+
multivariate_probit/ifm.py,sha256=T-rTeqqzswyQwRag7umX_dfxAPYbqzmZAy9wtGMUpkc,6163
|
|
5
|
+
multivariate_probit/inner.py,sha256=vpsw7E4VRB469-XfEX3_0t1-Iv9inw6rE5zawP5ZacE,6067
|
|
6
|
+
multivariate_probit/linear.py,sha256=G_UkbrMhbRaBd1IuyoOpqWBWyGwdRFhPef-e5lb9tjw,3621
|
|
7
|
+
multivariate_probit/model.py,sha256=Ow20RfVxjxkSdC2dMJ3miF92vff7WnRzkwz2YZrCeRU,14515
|
|
8
|
+
multivariate_probit/results.py,sha256=O_BIxciM1yLhTrPyrQCyrijHprE8YpY-zjNvAaCfce0,2614
|
|
9
|
+
multivariate_probit-0.1.0.dist-info/METADATA,sha256=7rAo4q5-9YU4E1lPBK84Eiw_iRurwBM5IKSUwYfhwWs,7272
|
|
10
|
+
multivariate_probit-0.1.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
11
|
+
multivariate_probit-0.1.0.dist-info/licenses/LICENSE,sha256=m-ui5AWkPPkGDgkw-DBberoUPhaPElGjr7t_L4sz74M,1069
|
|
12
|
+
multivariate_probit-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mark Shipman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|