Skip to main content

Subset Mixture Model (SMM)

PyPI version License: MIT

SMM is an interpretable, uncertainty-aware regression method for data with categorical features. It learns a global convex mixture over subset-induced partition estimators—one per non-empty feature subset—and returns a full predictive distribution together with a transparent account of why each prediction has the value and the uncertainty it does.

SMM is a genuinely probabilistic method:

  • Cross-fitted weights — subset cell means are multiway target encodings, so the mixture weights are learned on out-of-fold statistics to avoid target leakage (small, high-order cells no longer get to memorize the response).
  • Conjugate Student-t cells — each cell uses a Normal-Inverse-Gamma posterior, so its predictive component is a Student-t that widens and grows heavy tails in sparse cells (the plug-in Gaussian is the large-sample limit).
  • Exact mixture inference — NLL, CDF, central intervals (by bisection) and CRPS are computed exactly from the mixture of Student-t components.
  • Four-part predictive variance — within-cell noise, cell-estimation uncertainty, subset-resolution disagreement, and weight uncertainty (Laplace).
  • Interpretation as diagnostics — the learned weights are summarized by entropy, effective number of subsets, order-level mass, and concentration, rather than claimed to be a sparse ANOVA decomposition.

Installation

pip install subset-mixture-model

Import as smm:

import smm

Requires torch>=2.0, numpy, pandas, scipy. plot_calibration also needs matplotlib.


Quickstart

import numpy as np
import pandas as pd
from smm import SMM

# --- synthetic data: a main effect (region) + an interaction (season x tier) ---
rng = np.random.default_rng(0)
N = 2000
region = rng.integers(0, 2, N)
season = rng.integers(0, 3, N)
tier   = rng.integers(0, 2, N)
y = 5.0 * region + 3.0 * (season == 1) * tier + rng.normal(0, 2.0, N)
df = pd.DataFrame({"region": region, "season": season, "tier": tier, "y": y})

train, val, test = df[:1400], df[1400:1700], df[1700:]
FEATURES, TARGET = ["region", "season", "tier"], "y"

# --- fit: cross-fitted weights + conjugate Student-t cells + Laplace UQ ---
model = SMM(FEATURES, TARGET, kappa0=1.0, lam=0.5).fit(train, val)

# --- point prediction and a full predictive distribution ---
mean = model.predict(test)
mean, std = model.predict_with_uncertainty(test)
lo, hi = model.interval(test, level=0.95)          # exact mixture interval

y_test = test[TARGET].values
print("RMSE :", np.sqrt(np.mean((mean - y_test) ** 2)).round(3))
print("NLL  :", round(model.nll(test, y_test), 3))
print("CRPS :", round(model.crps(test, y_test), 3))
print("cov95:", round(float(((y_test >= lo) & (y_test <= hi)).mean()), 3))

Why this prediction? Uncertainty decomposition

mean, std, aleatoric_std, epistemic_std = model.predict_with_uncertainty(
    test, return_components=True
)
# std**2 == aleatoric**2 + epistemic**2  (within-cell noise vs. everything else)

Which interactions matter? Global diagnostics

print(model.weight_table(top_k=5))     # subsets ranked by learned weight
print(model.diagnostics())             # H, N_eff, HHI, order-level mass M_k

diagnostics() returns the concentration of the weight distribution. Diffuse weights (large N_eff) are an honest signal that no sparse subset explanation dominates—prediction draws on many comparable resolutions—while concentrated weights identify a few dominant interactions.

Why this prediction? Local contributions

row = test.iloc[[0]]
print(model.explain(row))     # per-subset contributions; they sum to the prediction

What the model gives you

Method Returns
SMM(features, target, ...) estimator; key knobs kappa0 (shrinkage), alpha0,lam (order-aware prior), K (folds), mode ("nig"/"plugin"), max_order
.fit(df, val_df=None) cross-fit → optimize weights (early-stop on val_df) → Laplace
.predict(df) predictive mean
.predict_with_uncertainty(df, return_components=) mean, std [, aleatoric, epistemic]
.nll / .crps exact mixture proper scores
.interval(df, level) exact central interval via bisection on the mixture CDF
.weight_table / .diagnostics / .explain interpretability outputs

Lower-level building blocks are also exported: SubsetMaker, crossfit_components, SubsetMixturePredictor, compute_posterior_covariance, predict_with_uncertainty, NIGPrior, weight_table, weight_diagnostics, calibration_stats.

To reproduce the first-version plug-in behavior, use SMM(..., mode="plugin", cross_fit=False).


Subset priors, raw categoricals, and reports

Raw categorical columns are accepted directly — SMM encodes them internally and preserves labels, so explain() and cell_table() show arena=MSG, day=Saturday rather than integer codes.

Control the prior over subsets with a chainable SubsetPrior:

from smm import SMM, SubsetPrior

prior = (SubsetPrior.order_decay(lam=0.6, strength=0.5)          # favor low order
         .boost_subset(("arena", "day", "month"), factor=4.0)    # domain belief
         .penalize_order_ge(5, factor=0.2)
         .exclude_contains("leaky_feature"))                     # hard governance mask

model = SMM(features, target, subset_prior=prior).fit(train, val)

Bases: uniform, order_decay, prefer_orders. Soft modifiers: boost_subset, boost_features, boost_group_interactions, penalize_order_ge. Hard masks: exclude_contains, exclude_order_ge, include_only.

Inspection & governance utilities:

Method What it shows
.explain(row, centered=True, by_order=) per-subset (or per-order) local contributions as lift vs. baseline
.cell_table(subset, sort_by=) the learned NIG cells: n, posterior mean, predictive sd, shrinkage
.reliability_report(df) per-row valid-subset mass, effective cell support n_eff, fallback flag
.weight_intervals() / .order_mass_intervals() Laplace posterior intervals on weights / order mass M_k
.calibration_stats(df, y) / .pit(df, y) exact-mixture coverage and PIT values
.interval(df, include_weight_uncertainty=True) intervals inflated for mixture-weight uncertainty
compare_smm_variants(train, test, features, target) one-call table over plug-in / NIG / cross-fit / uniform / best-subset

Method summary

For each non-empty feature subset s, SMM groups the training data by the values of s and models each resulting cell with a conjugate Normal-Inverse-Gamma posterior, giving a Student-t predictive component. A single global simplex weight vector π over all subsets is learned by MAP under an (optionally order-aware) Dirichlet prior, using out-of-fold cell statistics. The predictive distribution is the mixture ∑ₛ πₛ · tₛ, and uncertainty in π is propagated by a Laplace approximation in the low-dimensional logit space.

SMM is intended for problems whose predictive structure is concentrated in a modest number of naturally categorical features (full powerset for D ≤ 8; order-restricted for larger D). It is a transparent, uncertainty-aware alternative to gradient-boosted trees in that regime, not a general replacement.


Citation

@article{danielson2026smm,
  title   = {Subset Mixture Models: Interpretable Probabilistic Aggregation
             of Partition Estimators for Categorical Regression},
  author  = {Danielson, Aaron John},
  journal = {Under review},
  year    = {2026},
}

License

MIT © Aaron John Danielson

Metadata

Release files for subset-mixture-model 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for subset-mixture-model 1.0.0
File Size Uploaded
subset_mixture_model-1.0.0.tar.gz 30.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for subset-mixture-model 1.0.0
File Interpreter ABI Platform
subset_mixture_model-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 58.6 kB

Release files / subset_mixture_model-1.0.0.tar.gz

Download URL subset_mixture_model-1.0.0.tar.gz
Size 30.1 kB
Tags Source
SHA-256 checksum
How to use checksums
df880f1635d233368f5e3e23f3e0faa40bfbcdcca81afea1f38192b731a19b81
BLAKE2b-256 checksum
How to use checksums
427f0a6dde392d2b0f99e616c696617c05052871605a9d5b836edad4d51ffa47
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.10.13

Release files / subset_mixture_model-1.0.0-py3-none-any.whl

Download URL subset_mixture_model-1.0.0-py3-none-any.whl
Size 28.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
82ffe3aca867536d529d49ef24fadadfcdb9c0d6fbbce03ad472397571603cc5
BLAKE2b-256 checksum
How to use checksums
6b3962e34ee626ecb1fd81b30ab0c1713b94e5708702191cc883389ee581ce88
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.10.13

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release 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