Mondrian (class-conditional) conformal classification.

Classes

MondrianConformalClassifier

class uncertainty.conformal.mondrian.MondrianConformalClassifier(SplitConformalClassifier)

Conformal sets with per-group coverage, not just marginal coverage.

Plain split conformal guarantees 1 - \alpha coverage on average. That average can hide a rare class covered 40% of the time behind a common one covered 99%. Mondrian conformal prediction calibrates a separate threshold within each group — by default each class — so the guarantee holds inside every group independently.
Constructor
__init__(
    self,
    estimator: Any,
    alpha: float = 0.1,
    score: str = 'lac',
    calibration_size: float = 0.25,
    random_state: Optional[int] = None) -> None,
)

Overview

  1. Split off a calibration set as usual.
  2. Partition the calibration samples by their taxonomy — their true
class, or a caller-supplied group id.
  1. Compute a separate conformal quantile within each group.
  2. A test label joins the prediction set when its nonconformity falls below
its own group's threshold.

Theory

For every group g with calibration scores S_g and threshold \hat{q}_g, the guarantee is

P\left( Y_{n+1} \in C(X_{n+1}) \mid Y_{n+1} = g \right) \geq 1 - \alpha

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
Classifier
A TuiML classifier exposing predict_proba.
alpha
float = 0.1
Miscoverage level, enforced within every group.
score
{'lac', 'margin'} = 'lac'
Nonconformity score.
calibration_size
float = 0.25
Fraction of the training data held out for calibration.
random_state
int
Seed for the train/calibration split.

Attributes

classes_
np.ndarray of shape (n_classes,)
Class labels seen during fit.
group_quantiles_
dict
Mapping of group id to its conformal threshold. Groups too small to certify the level map to np.inf.
group_sizes_
dict
Number of calibration samples per group, for diagnosing which groups fell back to the conservative threshold.
scores_
np.ndarray of shape (n_calibration,)
Nonconformity scores across all groups.
fitted_
bool
Whether 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

Vovk2003
Vovk, V., Lindsay, D., Nouretdinov, I., & Gammerman, A. (2003). Mondrian Confidence Machine. Technical Report, Royal Holloway University of London.
Lofstrom2015
Löfström, T., Boström, H., Linusson, H., & Johansson, U. (2015). Bias Reduction through Conditional Conformal Prediction. Intelligent Data Analysis, 19(6), 1355-1375. :doi:`10.3233/IDA-150786`
python
>>> 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 the estimator and calibrate one threshold per group.

Parameters
X
np.ndarray of shape (n_samples, n_features)
Training features.
y
np.ndarray of shape (n_samples,)
Training labels.
groups
np.ndarray of shape (n_samples,)
Taxonomy assigning each sample to a group. Defaults to the class label, giving class-conditional coverage.
Returns
self
MondrianConformalClassifier
The fitted predictor.
predict_set (self, X: np.ndarray) -> np.ndarray

Predict class sets using each class's own threshold.

Parameters
X
np.ndarray of shape (n_samples, n_features)
Test features.
Returns
include
np.ndarray of shape (n_samples, n_classes) of bool
Class-membership mask, thresholded per class.
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
np.ndarray of shape (n_samples, n_features)
Test features.
groups
np.ndarray of shape (n_samples,)
Group id of each test sample, matching the taxonomy given to fit.
Returns
include
np.ndarray of shape (n_samples, n_classes) of bool
Class-membership mask, thresholded by each sample's group.
get_parameter_schema (cls) -> Dict[str, Any]

Return JSON Schema for constructor parameters.