Mondrian (class-conditional) conformal classification.
Classes
class uncertainty.conformal.mondrian.MondrianConformalClassifier(SplitConformalClassifier)
Conformal sets with per-group coverage, not just marginal coverage.
__init__( self, estimator: Any, alpha: float = 0.1, score: str = 'lac', calibration_size: float = 0.25, random_state: Optional[int] = None) -> None, )
Overview
- Split off a calibration set as usual.
- Partition the calibration samples by their taxonomy — their true
- Compute a separate conformal quantile within each group.
- A test label joins the prediction set when its nonconformity falls below
Theory
For every group g with calibration scores S_g and threshold \hat{q}_g, the guarantee is
which is strictly stronger than the marginal statement. The cost is statistical: each group needs its own calibration sample, so a group with fewer than \lceil 1/\alpha \rceil - 1 members cannot certify the level and falls back to always being included — conservative but valid.
Parameters
estimator
predict_proba.
alpha
score
calibration_size
random_state
Attributes
classes_
fit.
group_quantiles_
np.inf.
group_sizes_
scores_
fitted_
fit has been called.
Notes
Complexity. One estimator fit plus O(n \log n) total across groups — the same as split conformal.
When to use. Use Mondrian whenever a per-class or per-subgroup guarantee matters: imbalanced classification, fairness constraints across a protected attribute, or any setting where a regulator asks about a specific subpopulation rather than the average. Sets are wider than the marginal version — that width is the honest price of the stronger claim. Check group_sizes_ after fitting; a group with a handful of calibration samples silently gets a conservative threshold.
References
See Also
>>> import numpy as np
>>> from tuiml.uncertainty import MondrianConformalClassifier
>>> from tuiml.algorithms.trees import DecisionTreeClassifier
>>> rng = np.random.default_rng(0)
>>> X = rng.normal(size=(600, 4))
>>> y = (X[:, 0] + X[:, 1] > 0).astype(int)
>>> cp = MondrianConformalClassifier(DecisionTreeClassifier(max_depth=4),
... alpha=0.1, random_state=0)
>>> cp.fit(X, y)
MondrianConformalClassifier(estimator=DecisionTreeClassifier(), alpha=0.1)
>>> sorted(cp.group_sizes_.values()) == sorted(cp.group_sizes_.values())
True
>>> cp.predict_set(X[:5]).shape
(5, 2)
Methods
fit
(self, X: np.ndarray, y: np.ndarray, groups: Optional[np.ndarray]=None) -> 'MondrianConformalClassifier'
fit
(self, X: np.ndarray, y: np.ndarray, groups: Optional[np.ndarray]=None) -> 'MondrianConformalClassifier'
Fit the estimator and calibrate one threshold per group.
Parameters
X
y
groups
Returns
self
predict_set_for_groups
(self, X: np.ndarray, groups: np.ndarray) -> np.ndarray
predict_set_for_groups
(self, X: np.ndarray, groups: np.ndarray) -> np.ndarray
Predict class sets using an explicit per-sample group id.
Parameters
X
groups
fit.
Returns
include