Isotonic probability calibration via the pool-adjacent-violators algorithm.
Classes
Non-parametric probability calibration by isotonic regression.
PlattCalibrator it assumes no functional form — only that a higher score should never mean a lower probability — so it corrects arbitrary monotone distortions.__init__( self, out_of_bounds: str = 'clip', increasing: bool = True) -> None, )
Overview
- Sort the held-out calibration scores in increasing order.
- Run the pool-adjacent-violators algorithm (PAVA) on the paired
-
Store the resulting step function as
(thresholds_, values_). - At transform time, interpolate a new score into that step function.
Theory
Given calibration pairs (s_i, y_i) sorted by score, isotonic regression solves the constrained least-squares problem
PAVA solves this exactly in O(n) by maintaining a stack of blocks with non-decreasing means; whenever a new value violates the order, the offending blocks are pooled into their weighted average.
Because the fit is piecewise constant with at most n levels, isotonic calibration is more expressive than a sigmoid but needs more calibration data — roughly 1000 samples before it beats Platt scaling.
Parameters
out_of_bounds
'clip' extends the boundary probabilities; 'nan' returns np.nan.
increasing
Attributes
thresholds_
values_
classes_
fit.
fitted_
fit has been called.
Notes
Complexity. Fitting is O(n \log n) (dominated by the sort; PAVA itself is O(n)), transform is O(m \log n) via binary search. The PAVA step runs in the shared C++ kernel tuiml._cpp_ext.stats.pool_adjacent_violators.
When to use. Prefer isotonic when the calibration set is large (\gtrsim 1000 samples) or the miscalibration is not sigmoidal — for example the systematic over-confidence of boosted ensembles. Prefer Platt scaling on small calibration sets, where isotonic overfits.
References
See Also
>>> import numpy as np
>>> from tuiml.uncertainty import IsotonicCalibrator
>>> scores = np.array([0.1, 0.2, 0.35, 0.4, 0.65, 0.7, 0.8, 0.95])
>>> y = np.array([0, 0, 0, 1, 0, 1, 1, 1])
>>> cal = IsotonicCalibrator()
>>> proba = cal.fit_transform(scores, y)
>>> bool(np.all(np.diff(proba) >= 0))
True
>>> float(cal.transform(np.array([0.9]))[0])
1.0
Methods
fit
(self, scores: np.ndarray, y: np.ndarray, sample_weight: Optional[np.ndarray]=None) -> 'IsotonicCalibrator'
fit
(self, scores: np.ndarray, y: np.ndarray, sample_weight: Optional[np.ndarray]=None) -> 'IsotonicCalibrator'
Fit the isotonic calibration map on held-out scores.
Parameters
scores
y
sample_weight
Returns
self