Skip to main content

multivariate-probit

Multivariate probit models for correlated binary outcomes, with a pluggable inner model.

The model

Each outcome is a threshold on a latent Gaussian variable, and the outcomes are tied together by the correlation of those latents:

Y_j = 1[ η_j(x) + e_j > 0 ],    e ~ N(0, Σ),    j = 1 … d

η_j is an arbitrary real-valued function of the features — linear by default, or XGBoost, a random forest, or anything else that fits (X, y). Σ is a correlation matrix (unit diagonal) carrying the dependence between outcomes. Marginally, P(Y_j = 1 | x) = Φ(η_j(x)).

Fitting is cross-fit two-stage IFM: every margin is fitted independently, then Σ is estimated by maximum likelihood with those margins held fixed. That separation is what lets the inner model be a black box. The algorithm, and the alternatives that were tested and rejected, are in docs/ifm.md.

Install

pip install multivariate-probit           # core: numpy + scipy
pip install multivariate-probit[xgboost]  # adds the "xgboost" preset
pip install multivariate-probit[all]      # every preset

Quickstart

from multivariate_probit import MultivariateProbit

model = MultivariateProbit(inner="linear").fit(X, Y)   # Y is (n, d), 0/1

proba = model.predict_proba(X)
proba.marginal                  # P(Y_j = 1 | x), shape (n, d)
proba.joint([1, 0, 1])          # P(Y = pattern | x), shape (n,)
proba.all()                     # P(every outcome = 1 | x)
proba.any(outcomes=[0, 2])      # P(at least one of these | x)

model.correlation_              # the fitted Σ, shape (d, d)
model.transform(X)              # latent scores η, shape (n, d)
model.sample(X, n_samples=100)  # simulated outcome patterns
model.score(X, Y)               # mean joint log-likelihood

predict_proba returns an object that behaves like the marginal-probability array (np.asarray(proba), indexing, .shape) and additionally answers the joint questions Σ was estimated for. Marginal predictions do not involve Σ at all; everything joint does.

Everything here is a squashing function over a latent index

The inner model never sees a probability, and never sees another outcome's labels. It produces an unbounded score η_j(x) on (-∞, ∞); Φ is the only squashing function applied to it. Any estimator that emits a real-valued score, or a probability that can be pushed back through Φ⁻¹, is a legal margin.

That is the whole abstraction, and it is why the inner model is swappable without touching the estimation code.

Inner models

Preset Estimator Requires
"linear" (default), "probit" native probit via IRLS
"xgboost", "xgb" XGBClassifier, tuned for calibration xgboost
"rf", "random_forest" RandomForestClassifier scikit-learn
MultivariateProbit(inner="xgboost")                        # a preset
MultivariateProbit(inner=XGBClassifier(max_depth=4))       # any sklearn-shaped model
MultivariateProbit(inner=["linear", "xgboost", "linear"])  # one per outcome

available_inners() lists the presets; register_inner(name, factory) adds your own. See docs/api.md for the inner-model contract.

Two knobs that cost time

  • dependence"joint" (default) maximises the full d-variate likelihood for Σ. "pairwise" maximises each pair's bivariate likelihood instead: consistent, orders of magnitude cheaper, and the right choice once you have more than a handful of outcomes.
  • cv5 by default, cross-fitting the margins so Σ is never estimated from in-sample predictions. This is not optional hygiene: in-sample margins drive every fitted correlation to +1. cv=None skips it, which is defensible for the linear default and reckless for anything that can overfit.

Status

Alpha. The linear and XGBoost paths are covered by tests; the rf preset is wired but untested. Standard errors are not computed — correlation_ is a point estimate. Known gaps are listed in docs/limitations.md.

Documentation

Development

pip install -e ".[dev]"
pytest

License

MIT. See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

multivariate_probit-0.1.0.tar.gz (34.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

multivariate_probit-0.1.0-py3-none-any.whl (21.6 kB view details)

Uploaded Python 3

File details

Details for the file multivariate_probit-0.1.0.tar.gz.

File metadata

  • Download URL: multivariate_probit-0.1.0.tar.gz
  • Upload date:
  • Size: 34.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.9

File hashes

Hashes for multivariate_probit-0.1.0.tar.gz
Algorithm Hash digest
SHA256 a4018d7dec31b154210d9b40a3b3291ad47fa739c1094a55e6d470ebbfbd4dc3
MD5 0b31d193d88f70b9df233e925beec285
BLAKE2b-256 8ddd81edd6437eb2f1dc82412c80e4477fbf85c7d88ffcce57f3b28c6df5200c

See more details on using hashes here.

File details

Details for the file multivariate_probit-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for multivariate_probit-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 abf1f9df24cef1f4cfdf527449b6089f924709d05db94b296f81e000dae898b1
MD5 9f99d2496d40d287e05982726288f1d1
BLAKE2b-256 7e0c6a4ebdbd38b2458cd5e598d7242dc008488d56dfd99f56d9051c6a944e0c

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page