Calibration Metrics¶
This module provides metrics for evaluating calibration quality.
For the CORP reliability diagram and the MCB/DSC/UNC score
decomposition — which need no bin count and cannot be tuned in your favor —
see CORP Evaluation.
Bias-aware calibration error¶
The plugin binned estimator is biased upward: part of each bin’s gap is sampling noise in the label mean rather than miscalibration, and the bias grows with the bin count. These two estimators correct for that, and are the ones to reach for when the number will be reported.
Smooth Calibration Error (smECE)¶
- calibre.smooth_calibration_error(y_true: ndarray, y_pred: ndarray, *, bandwidth: float | None = None, return_bandwidth: Literal[False] = False) float[source]¶
- calibre.smooth_calibration_error(y_true: ndarray, y_pred: ndarray, *, bandwidth: float | None = None, return_bandwidth: Literal[True]) tuple[float, float]
Calculate the smooth calibration error (smECE).
Binned calibration error has no consistent limit: refine the bins and the estimate keeps climbing on data that is perfectly calibrated. smECE replaces the bins with a Gaussian kernel and, by default, chooses its own bandwidth. The default estimator therefore has no bin-count or bandwidth choice for the caller to tune.
\[\mathrm{smECE}_\sigma(f, y) = \frac{\int \left| (K_\sigma \star \nu)(t) \right| \, dt} {\int (K_\sigma \star \rho)(t) \, dt}, \qquad \nu = \sum_i (f_i - y_i)\, \delta_{f_i}, \quad \rho = \sum_i \delta_{f_i}\]The bandwidth is the fixed point \(\sigma = \mathrm{smECE}_\sigma\), found by bisection. Below that width the kernel is resolving noise; above it, it is smoothing away real miscalibration.
This is a consistent calibration measure in the sense of Blasiok, Gopalan, Hu and Nakkiran (2023): it is bounded above and below by polynomial functions of the true distance to the nearest perfectly calibrated predictor. Binned ECE is not, which is why it can report a large error for a predictor that is almost calibrated and a small one for a predictor that is not.
Evaluate smECE on held-out predictions. The automatic bandwidth is selected from that evaluation sample as part of the estimator; it is not a calibrator fit and does not require a separate tuning split. To assess recalibration, pair smECE with held-out proper scores and resolution or discrimination diagnostics rather than selecting a model from calibration error alone.
- Parameters:
y_true (ndarray) – Binary outcomes in
{0, 1}.y_pred (ndarray) – Predicted probabilities in
[0, 1].bandwidth (float | None) – Kernel bandwidth, at least
0.001. When None, the fixed point above is used, which is the recommended behavior and what makes the estimator hyperparameter-free. A fixed bandwidth is useful when its scale has a substantive interpretation, with weaker theoretical guarantees than the automatic choice.return_bandwidth (bool) – Also return the bandwidth used. Worth reporting: it is an interpretable scale, roughly the resolution at which miscalibration is detectable.
- Returns:
- The smooth calibration error, and
the bandwidth when
return_bandwidth.
- Return type:
- Raises:
ValueError – If inputs are empty, multidimensional, non-finite, inconsistent in shape, outside their documented domains, or non-numeric; or if
bandwidthis not a finite number at least0.001.TypeError – If
return_bandwidthis not boolean.
See also
debiased_calibration_error()– bias-corrected, but still needs a bin count.calibre.evaluation.score_decomposition()– avoids binning by using isotonic regression, and decomposes the score rather than summarizing the error.
References
Błasiok & Nakkiran (2024), “Smooth ECE: Principled Reliability Diagrams via Kernel Smoothing”, ICLR. Błasiok, Gopalan, Hu & Nakkiran (2023), “A Unifying Theory of Distance from Calibration”, STOC. The numerical discretization and bandwidth search follow Błasiok and Nakkiran’s Apple
relplotreference implementation: https://github.com/apple/ml-calibration. SeeTHIRD_PARTY_NOTICES.md.Examples
>>> import numpy as np >>> rng = np.random.default_rng(0) >>> p = rng.uniform(0, 1, 2000) >>> y = rng.binomial(1, p).astype(float)
Calibrated by construction, so the error is near zero:
>>> bool(smooth_calibration_error(y, p) < 0.03) True
An overconfident predictor is caught:
>>> squashed = np.clip(2.0 * (p - 0.5) + 0.5, 0, 1) >>> bool(smooth_calibration_error(y, squashed) > 0.05) True
Unlike a binned estimator there is no bin count to justify; the bandwidth is chosen by the data:
>>> error, width = smooth_calibration_error(y, p, return_bandwidth=True) >>> bool(0.0 < width <= 1.0) True
Unlike everything below it, the default smECE has no bin count and selects its own bandwidth. It is a consistent measure of distance from calibration and is the one to reach for when the number will be quoted without qualification.
Debiased Calibration Error¶
- calibre.debiased_calibration_error(y_true, y_pred, *, n_bins=15, squared=False)[source]¶
Calculate the debiased \(\ell_2\) calibration error.
The plugin binned estimator mistakes sampling noise in each bin’s outcome rate for miscalibration. This estimator subtracts that Bernoulli-noise term from the squared gap before aggregating bins. It removes this finite-sample component of bias; it does not remove the approximation error caused by representing a continuous calibration curve with finitely many bins.
\[\widehat{\mathrm{CE}}^2 = \sum_k \frac{n_k}{n} \left[ (\bar{f}_k - \bar{y}_k)^2 - \frac{\bar{y}_k (1 - \bar{y}_k)}{n_k - 1} \right]\]- Parameters:
y_true (ndarray) – Ground truth values (0 or 1).
y_pred (ndarray) – Predicted probabilities.
n_bins (int) – Requested number of equal-mass bins, capped at the sample size. Tied predictions are never split, so fewer bins may be used. The conventional default of 15 follows the experiments in Roelofs et al.; it is not universally optimal.
squared (bool) – Return the estimate of the squared error instead, without the square root or the floor at zero. This is the quantity the correction actually makes unbiased, and it may legitimately come out negative – see Notes.
- Returns:
- Debiased calibration error. Floored at zero: the correction can
drive the sum negative on well-calibrated data, which is evidence of no detectable miscalibration rather than of negative error. With
squared=Truethe unfloored sum is returned instead.
- Return type:
- Raises:
ValueError – If inputs are empty, multidimensional, non-finite, inconsistent in length, or outside their documented domains; if
n_binsis not a positive integer; or ifsquaredis not boolean.
Notes
This is the \(\ell_2\) error, so it is not comparable in magnitude to
expected_calibration_error(), which is \(\ell_1\).The correction operates on the squared scale, not the root scale. On well-calibrated samples, the corrected squared estimate fluctuates around zero and can legitimately be negative. The default floor and square root produce a reportable non-negative error but reintroduce upward bias near zero. For averaging or model comparison, use
squared=Trueand aggregate on that scale.sample_weightis deliberately unsupported. The cited correction is derived for individual independent observations; treating arbitrary weights as replication or importance weights would change that statistical claim.References
Bröcker (2012), “Estimating Reliability and Resolution of Probability Forecasts through Decomposition of the Empirical Score”; Ferro & Fricker (2012), “A Bias-Corrected Decomposition of the Brier Score”. The formula follows Kumar, Liang & Ma’s
unbiased_square_ceandunbiased_l2_cereference implementation for “Verified Uncertainty Calibration” (NeurIPS 2019). Equal-mass binning follows the empirical recommendation of Roelofs, Cain, Shlens & Mozer (2022), “Mitigating Bias in Calibration Error Estimation”.Examples
>>> import numpy as np >>> rng = np.random.default_rng(0) >>> p = rng.uniform(0, 1, 4000) >>> y = rng.binomial(1, p).astype(float)
These predictions are calibrated, so the matched \(\ell_2\) plugin estimator reports finite-sample noise that the correction largely removes:
>>> plugin = plugin_calibration_error(y, p, n_bins=15, norm=2) >>> debiased = debiased_calibration_error(y, p, n_bins=15) >>> bool(debiased < plugin) True
See also
sweep_calibration_error : Chooses the bin count instead of fixing it. calibre.evaluation.score_decomposition : Avoids binning altogether.
Sweep Calibration Error¶
- calibre.sweep_calibration_error(y_true: ndarray, y_pred: ndarray, *, norm: int = 2, return_n_bins: Literal[False] = False) float[source]¶
- calibre.sweep_calibration_error(y_true: ndarray, y_pred: ndarray, *, norm: int = 2, return_n_bins: Literal[True]) tuple[float, int]
Calculate the monotonic sweep calibration error (
ECE_sweep).Fixing the bin count is the weak point of binned calibration error: too few bins hide miscalibration, too many measure noise, and the best choice depends on the sample size and the score distribution. This estimator chooses instead.
The method assumes a non-decreasing population calibration curve, as is often expected for likelihood-trained models. Bins are added while the observed bin heights stay monotone, and the sweep stops at the largest bin count for which they do. Under that assumption, non-monotonicity signals that the bins have become fine enough to read sampling noise. For a genuinely nonmonotone calibration curve, the method can stop too early and understate error.
- Parameters:
y_true (ndarray) – Ground truth values (0 or 1).
y_pred (ndarray) – Predicted probabilities.
norm (int) – Positive integer norm. The default is 2, matching the paper’s experiments and the authors’ reference implementation. Set to 1 for the weighted mean absolute gap.
return_n_bins (bool) – Also return the bin count the sweep settled on. That number is half of what the estimator has to say – it is the sweep’s answer to “how fine can these data support?” – and reporting only the error hides it.
- Returns:
- Binned calibration error at the
selected bin count, and that bin count when
return_n_binsis True. The count is the number of bins actually occupied, which ties can hold below the number the sweep reached.
- Return type:
- Raises:
ValueError – If inputs are empty, multidimensional, non-finite, inconsistent in length, or outside their documented domains; if
normis not a positive integer; or ifreturn_n_binsis not boolean.
Notes
This function evaluates held-out predictions; it does not fit a calibrator. The selected bin count and error are properties of this evaluation sample.
sample_weightis deliberately unsupported. The cited algorithm defines equal mass as equal numbers of independent observations; arbitrary weights require a separate definition of both bin mass and monotonicity.References
Roelofs, Cain, Shlens & Mozer (2022), “Mitigating Bias in Calibration Error Estimation”, AISTATS. The implementation follows Algorithm 1 and the authors’ Google Research
em_monotonic_sweepreference, with the additional guarantee that tied predictions are never split.Examples
>>> import numpy as np >>> rng = np.random.default_rng(0) >>> p = rng.uniform(0, 1, 4000) >>> y = rng.binomial(1, p).astype(float) >>> float(sweep_calibration_error(y, p)) < 0.05 True
See also
debiased_calibration_error : Fixes the bin count and corrects the bias. calibre.evaluation.score_decomposition : Lets isotonic regression bin.
Plugin Calibration Error¶
- calibre.plugin_calibration_error(y_true, y_pred, *, n_bins=15, norm=2, sample_weight=None)[source]¶
Calculate the uncorrected \(\ell_p\) binned calibration error.
\[\widehat{\mathrm{CE}}_p = \left[ \sum_k \frac{W_k}{W} \left| \bar{f}_k - \bar{y}_k \right|^p \right]^{1/p}\]Here \(W_k\) is the total evaluation weight in bin \(k\); with unit weights, \(W_k/W = n_k/n\).
This is the plain plugin estimator: the quantity
debiased_calibration_error()corrects andsweep_calibration_error()chooses a bin count for. It exists so those three can be compared on equal terms.That comparison is otherwise a trap.
expected_calibration_error()is \(\ell_1\) on uniform-width bins, whiledebiased_calibration_error()andsweep_calibration_error()default to \(\ell_2\) on equal-mass bins. This function takes both the norm and bin count as arguments and uses the same equal-mass, tie-safe binning as the bias-aware estimators, so callers can compare matched quantities.- Parameters:
y_true (ndarray) – Ground truth values (0 or 1).
y_pred (ndarray) – Predicted probabilities.
n_bins (int) – Number of equal-mass bins. Fewer are used when ties prevent it.
norm (int) – Norm. 1 gives the familiar weighted mean absolute gap; 2 matches
debiased_calibration_error().sample_weight (ndarray | None) – Non-negative evaluation weights. Zero-weight rows are excluded before validation and binning.
- Returns:
- The uncorrected calibration error. Biased upward, and
increasingly so as
n_binsgrows.
- Return type:
- Raises:
ValueError – If inputs are empty, multidimensional, non-finite, inconsistent in length, outside their documented domains, or carry invalid weights; or if
n_binsornormis not a positive integer.
References
Kumar, Liang & Ma (2019), “Verified Uncertainty Calibration”, NeurIPS 32. The formula follows the authors’
plugin_cereference implementation; the 15-bin default follows theirlower_bound_scaling_ceinterface. Roelofs, Cain, Shlens & Mozer (2022), “Mitigating Bias in Calibration Error Estimation”, AISTATS, PMLR 151:4036–4054.See also
debiased_calibration_error : The same quantity at
norm=2, bias-corrected. sweep_calibration_error : Choosesn_binsrather than fixing it.Examples
>>> import numpy as np >>> rng = np.random.default_rng(0) >>> p_hat = rng.uniform(0, 1, 4000) >>> y = rng.binomial(1, p_hat).astype(float)
These are calibrated by construction, so the true error is zero and whatever the plugin reports is bias – which grows with the bin count:
>>> coarse = plugin_calibration_error(y, p_hat, n_bins=5) >>> fine = plugin_calibration_error(y, p_hat, n_bins=50) >>> bool(fine > coarse) True
Debiasing removes it:
>>> bool(debiased_calibration_error(y, p_hat, n_bins=50) < fine) True
Calibration Error Metrics¶
Mean Calibration Error¶
- calibre.mean_calibration_error(y_true, y_pred, *, sample_weight=None)[source]¶
Calculate the absolute calibration-in-the-large error.
\[\left| \frac{\sum_i w_i \hat p_i}{\sum_i w_i} - \frac{\sum_i w_i y_i}{\sum_i w_i} \right|\]This measures whether predictions are right on average. It can be zero when positive and negative errors cancel, even for completely reversed predictions, so use it with a proper score and a calibration curve rather than as a complete calibration assessment.
- Parameters:
- Returns:
Absolute difference between the mean prediction and the base rate.
- Return type:
References
Van Calster, B., Nieboer, D., Vergouwe, Y., De Cock, B., Pencina, M. J., & Steyerberg, E. W. (2016). A calibration hierarchy for risk models was defined: from utopia to empirical data. Journal of Clinical Epidemiology, 74, 167–176. https://doi.org/10.1016/j.jclinepi.2015.12.005
Examples
>>> import numpy as np >>> y_true = np.array([0, 1, 1, 0, 1]) >>> y_pred = np.array([0.2, 0.7, 0.8, 0.4, 0.6]) >>> round(mean_calibration_error(y_true, y_pred), 4) # mean 0.54 vs base 0.6 0.06
A perfectly calibrated predictor scores zero, however unsharp it is:
>>> y = np.array([0, 0, 1, 1]) >>> mean_calibration_error(y, np.full(4, 0.5)) 0.0
Root Mean Squared Calibration Error¶
- calibre.root_mean_squared_calibration_error(y_true: ndarray, y_pred: ndarray, *, n_bins: int = 10, strategy: Literal['uniform', 'quantile'] = 'uniform', sample_weight: ndarray | None = None, return_details: Literal[False] = False) float[source]¶
- calibre.root_mean_squared_calibration_error(y_true: ndarray, y_pred: ndarray, *, n_bins: int = 10, strategy: Literal['uniform', 'quantile'] = 'uniform', sample_weight: ndarray | None = None, return_details: Literal[True]) dict[str, float | ndarray]
Calculate the mass-weighted root mean squared calibration error.
\[\operatorname{RMSCE} = \left[\sum_k \frac{w_k}{\sum_j w_j} (\bar p_k - \bar y_k)^2\right]^{1/2}\]Unlike an unweighted average across occupied bins, this definition gives a bin influence proportional to its evaluation-sample mass. Uniform bins divide the probability domain
[0, 1]into equal-width intervals. Quantile bins are approximately equal in weighted mass and never split tied predictions.This is a plugin estimator: its value depends on the binning choice and is biased upward by finite-sample variation in bin outcome rates. Use the same held-out evaluation observations for comparisons between prediction systems; do not evaluate a calibrator on the observations used to fit it.
- Parameters:
y_true (ndarray) – Binary outcomes in
{0, 1}.y_pred (ndarray) – Predicted probabilities in
[0, 1].n_bins (int) – Requested number of bins, capped at the number of positive-weight observations. Quantile binning can use fewer bins when ties prevent a split.
strategy (Literal['uniform', 'quantile']) –
"uniform"for fixed-width probability bins or"quantile"for weighted equal-mass, tie-preserving bins.sample_weight (ndarray | None) – Non-negative evaluation weights. Zero-weight observations are ignored. A common rescaling does not change the result.
return_details (bool) – Return the score and occupied-bin summaries when true.
- Returns:
RMSCE when
return_detailsis false. dict: RMSCE, counts, weights, prediction ranges, and weighted bin means whenreturn_detailsis true.- Return type:
- Raises:
ValueError – If inputs are empty, multidimensional, non-finite, inconsistent in length, outside their documented domains, or carry invalid weights; if
n_binsis not a positive integer; or ifstrategyis unknown.TypeError – If
return_detailsis not boolean.
References
Guo, Pleiss, Sun & Weinberger (2017), “On Calibration of Modern Neural Networks”, ICML, PMLR 70:1321–1330. Kumar, Liang & Ma (2019), “Verified Uncertainty Calibration”, NeurIPS 32. The formula matches the
norm="l2"calibration error implemented by TorchMetrics.Examples
>>> import numpy as np >>> y_true = np.array([0, 0, 1, 1]) >>> y_pred = np.array([0.25, 0.25, 0.75, 0.75]) >>> root_mean_squared_calibration_error(y_true, y_pred, n_bins=2) 0.25
Expected Calibration Error¶
- calibre.expected_calibration_error(y_true, y_pred, *, n_bins=10, sample_weight=None)[source]¶
Calculate uniform-bin expected calibration error (ECE).
\[\operatorname{ECE} = \sum_k \frac{w_k}{\sum_j w_j} \left|\bar p_k - \bar y_k\right|\]The probability interval
[0, 1]is divided into equal-width bins, and each occupied bin’s absolute calibration gap is weighted by its evaluation mass. This is the conventional plugin ECE estimator, matching thenorm="l1"definition in TorchMetrics.ECE is sensitive to the bin count and is not a consistent measure of all calibration departures: overprediction and underprediction inside one bin can cancel exactly. Finite-sample variation in bin outcome rates also biases the score upward. Use
smooth_calibration_error(),debiased_calibration_error(), orsweep_calibration_error()when a less arbitrary headline estimate is required.- Parameters:
y_true (ndarray) – Binary outcomes in
{0, 1}.y_pred (ndarray) – Predicted probabilities in
[0, 1].n_bins (int) – Requested number of equal-width bins, capped at the number of positive-weight observations.
sample_weight (ndarray | None) – Non-negative evaluation weights. Zero-weight observations are ignored. A common rescaling does not change the result.
- Returns:
Sample-mass-weighted absolute calibration gap.
- Return type:
- Raises:
ValueError – If inputs are empty, multidimensional, non-finite, inconsistent in length, outside their documented domains, or carry invalid weights; or if
n_binsis not a positive integer.
References
Naeini, Cooper & Hauskrecht (2015), “Obtaining Well Calibrated Probabilities Using Bayesian Binning”, AAAI 29. Guo, Pleiss, Sun & Weinberger (2017), “On Calibration of Modern Neural Networks”, ICML, PMLR 70:1321–1330. Roelofs, Cain, Shlens & Mozer (2022), “Mitigating Bias in Calibration Error Estimation”, AISTATS, PMLR 151:4036–4054.
Examples
>>> import numpy as np >>> y_true = np.array([0, 0, 1, 1, 1]) >>> y_pred = np.array([0.25, 0.25, 0.55, 0.75, 0.75]) >>> round(expected_calibration_error(y_true, y_pred, n_bins=2), 2) 0.29
Maximum Calibration Error¶
- calibre.maximum_calibration_error(y_true, y_pred, *, n_bins=10, sample_weight=None)[source]¶
Calculate uniform-bin maximum calibration error (MCE).
\[\operatorname{MCE} = \max_k \left|\bar p_k - \bar y_k\right|\]The probability interval
[0, 1]is divided into equal-width bins. MCE is the largest absolute gap between the weighted mean prediction and weighted event rate among the occupied bins. This is the conventional infinity-norm plugin estimator, matching thenorm="max"definition in TorchMetrics.MCE gives the same influence to every occupied bin regardless of its evaluation mass: one low-mass bin can determine the entire score. It is also sensitive to the bin count, can hide equal and opposite errors within a bin, and selects the largest finite-sample fluctuation. Treat it as a worst-bin diagnostic rather than a standalone estimate of population calibration. Use held-out observations and inspect the bin counts in a reliability diagram.
- Parameters:
y_true (ndarray) – Binary outcomes in
{0, 1}.y_pred (ndarray) – Predicted probabilities in
[0, 1].n_bins (int) – Requested number of equal-width bins, capped at the number of positive-weight observations.
sample_weight (ndarray | None) – Non-negative evaluation weights used to calculate each bin’s means. Zero-weight observations are ignored. A common rescaling does not change the result.
- Returns:
Largest occupied-bin absolute calibration gap.
- Return type:
- Raises:
ValueError – If inputs are empty, multidimensional, non-finite, inconsistent in length, outside their documented domains, or carry invalid weights; or if
n_binsis not a positive integer.
References
Naeini, Cooper & Hauskrecht (2015), “Obtaining Well Calibrated Probabilities Using Bayesian Binning”, AAAI 29. Guo, Pleiss, Sun & Weinberger (2017), “On Calibration of Modern Neural Networks”, ICML, PMLR 70:1321–1330. Roelofs, Cain, Shlens & Mozer (2022), “Mitigating Bias in Calibration Error Estimation”, AISTATS, PMLR 151:4036–4054.
Examples
>>> import numpy as np >>> y_true = np.array([0, 0, 1, 1, 1]) >>> y_pred = np.array([0.25, 0.25, 0.55, 0.75, 0.75]) >>> round(maximum_calibration_error(y_true, y_pred, n_bins=2), 4) 0.3167
Note
These estimators are not interchangeable. By default,
expected_calibration_error() is \(\ell_1\), while
debiased_calibration_error() and
sweep_calibration_error() are \(\ell_2\).
expected_calibration_error() uses uniform-width bins, while the
two bias-aware estimators use equal-mass bins. Compare like with like by
setting the same norm where the APIs permit it.
Scoring Metrics¶
Brier Score¶
- calibre.brier_score(y_true, y_pred, *, sample_weight=None)[source]¶
Calculate the binary Brier score.
\[\operatorname{BS} = \frac{\sum_i w_i (p_i-y_i)^2}{\sum_i w_i}\]The Brier score is a strictly proper scoring rule: in expectation, reporting the true event probability uniquely minimises it. Lower is better, with zero for perfect binary probability forecasts and one for forecasts that are wrong with certainty.
Brier score measures overall probabilistic performance, not calibration alone. Its Murphy decomposition separates miscalibration, discrimination, and outcome uncertainty. Compare systems on the same held-out observations; do not evaluate a calibrator on the observations used to fit it.
- Parameters:
- Returns:
Weighted mean squared probability error.
- Return type:
References
Brier (1950), “Verification of Forecasts Expressed in Terms of Probability”, Monthly Weather Review 78:1–3. Murphy (1973), “A New Vector Partition of the Probability Score”, Journal of Applied Meteorology 12:595–600. The calculation delegates to
sklearn.metrics.brier_score_loss().Examples
>>> import numpy as np >>> y_true = np.array([0, 1, 1, 0, 1]) >>> y_pred = np.array([0.2, 0.7, 0.8, 0.4, 0.6]) >>> brier_score(y_true, y_pred) 0.098
Calibration Curve¶
- calibre.calibration_curve(y_true, y_pred, *, n_bins=10, strategy='uniform', sample_weight=None)[source]¶
Compute occupied-bin summaries for a binary reliability diagram.
Uniform bins divide the probability domain
[0, 1]into equal-width intervals. Quantile bins have approximately equal evaluation mass and never split tied predictions. Empty bins are omitted: returning them as zero-valued points would make missing data look like observed perfect predictions.The bin means use
sample_weightwhen supplied.bin_countsremains the number of positive-weight observations in each occupied bin, so it describes empirical support rather than weighted mass. Use held-out evaluation data; computing this curve on the observations used to fit a calibrator gives an optimistic picture of its performance.- Parameters:
y_true (ndarray) – Binary outcomes in
{0, 1}.y_pred (ndarray) – Predicted probabilities in
[0, 1].n_bins (int) – Requested number of bins, capped at the number of positive-weight observations. Quantile binning can use fewer bins when ties prevent a split.
strategy (Literal['uniform', 'quantile']) –
"uniform"for fixed-width probability bins or"quantile"for weighted equal-mass, tie-preserving bins.sample_weight (ndarray | None) – Non-negative evaluation weights. Zero-weight observations are ignored. A common rescaling changes neither the bin assignments nor the bin means, including with quantile binning.
- Returns:
Weighted event rate in each occupied bin. prob_pred: Weighted mean predicted probability in each occupied bin. bin_counts: Number of positive-weight observations in each occupied bin.
- Return type:
prob_true
- Raises:
ValueError – If inputs are empty, multidimensional, non-finite, inconsistent in length, outside their documented domains, or carry invalid weights; if
n_binsis not a positive integer; or ifstrategyis unknown.
References
Niculescu-Mizil & Caruana (2005), “Predicting Good Probabilities with Supervised Learning”, ICML, section 4. Guo, Pleiss, Sun & Weinberger (2017), “On Calibration of Modern Neural Networks”, ICML, PMLR 70:1321–1330. The unweighted API and occupied-bin convention follow
sklearn.calibration.calibration_curve().Examples
>>> import numpy as np >>> y_true = np.array([0, 0, 1, 1]) >>> y_pred = np.array([0.1, 0.2, 0.8, 0.9]) >>> prob_true, prob_pred, counts = calibration_curve( ... y_true, y_pred, n_bins=2 ... ) >>> prob_true array([0., 1.]) >>> prob_pred array([0.15, 0.85]) >>> counts array([2, 2])
Statistical Metrics¶
Correlation Metrics¶
- calibre.correlation_metrics(y_true, y_pred, *, input_scores=None, original_predictions=None)[source]¶
Calculate descriptive Spearman rank correlations.
These correlations diagnose monotonic association and rank preservation; they do not measure probability calibration. In particular, every strictly increasing transformation of a prediction vector has the same Spearman correlation even when the transformed probabilities are badly miscalibrated.
A correlation is undefined when either member of a pair is constant. The corresponding value is
NaNwithout emitting SciPy’s constant-input warning. Weighted Spearman correlation is intentionally unsupported because SciPy has no standard weighted definition.- Parameters:
- Returns:
Spearman correlations to the outcomes and any supplied comparison vectors.
- Return type:
References
Spearman (1904), “The Proof and Measurement of Association between Two Things”, American Journal of Psychology 15:72–101. The calculation delegates to
scipy.stats.spearmanr().Examples
>>> import numpy as np >>> y_true = np.array([0, 1, 1, 0, 1]) >>> y_pred = np.array([0.2, 0.7, 0.8, 0.4, 0.6]) >>> original = np.array([0.1, 0.6, 0.9, 0.3, 0.5]) >>> corr = correlation_metrics( ... y_true, y_pred, original_predictions=original ... ) >>> sorted(corr) ['spearman_corr_to_original_predictions', 'spearman_corr_to_y_true'] >>> round(float(corr["spearman_corr_to_y_true"]), 4) 0.866 >>> round(float(corr["spearman_corr_to_original_predictions"]), 4) 1.0
Granularity Metrics¶
These measure what a calibrator did to the resolution of your scores — the thing isotonic regression quietly destroys. No other calibration package reports them.
Unique Value Counts¶
- calibre.unique_value_counts(predictions, *, original_predictions=None)[source]¶
Count exact distinct values before and after calibration.
This is a structural granularity diagnostic, not a measure of calibration or statistical resolution. Counts are exact: the function does not silently merge nearby floating-point values. Apply domain-specific rounding explicitly before calling if operationally equivalent values should be grouped.
When original predictions are supplied, both vectors must describe the same observations. The ratio is the calibrated count divided by the original count; values below one indicate that calibration produced fewer distinct outputs.
- Parameters:
- Returns:
Exact distinct-value counts and, when available, their ratio.
- Return type:
References
Counting delegates to
numpy.unique(). Statistical resolution should instead be evaluated through a proper-score decomposition such asbrier_decomposition().Examples
>>> import numpy as np >>> predictions = np.array([0.2, 0.7, 0.7, 0.2, 0.7]) >>> original = np.array([0.1, 0.6, 0.9, 0.2, 0.5]) >>> counts = unique_value_counts( ... predictions, original_predictions=original ... ) >>> counts["n_unique_predictions"], counts["n_unique_original_predictions"] (2, 5) >>> counts["unique_prediction_ratio"] 0.4
Tie Preservation Score¶
- calibre.tie_preservation_score(original_predictions, calibrated_predictions)[source]¶
Measure pairwise agreement between original and calibrated tie partitions.
Each distinct prediction value defines a group. The returned Rand index is the fraction of observation pairs that are either tied in both vectors or distinct in both vectors. It is one when calibration leaves every pair’s tie status unchanged and zero when every pair’s status changes.
Equality is exact, matching
unique_value_counts(). This is a structural diagnostic, not a calibration or forecast-quality metric: prediction vectors with the same tie partition can have very different proper scores.- Parameters:
- Returns:
Rand index between zero and one.
- Return type:
References
Rand (1971), “Objective Criteria for the Evaluation of Clustering Methods”, Journal of the American Statistical Association 66(336), 846–850. The implementation delegates to
sklearn.metrics.rand_score().Examples
>>> import numpy as np >>> original = np.array([0.1, 0.2, 0.3, 0.4]) >>> calibrated = np.array([0.1, 0.1, 0.3, 0.4]) >>> tie_preservation_score(original, calibrated) 0.8333333333333334
Usage Examples¶
Basic Evaluation¶
import numpy as np
from calibre import (
brier_score,
expected_calibration_error,
mean_calibration_error,
)
y_true = np.array([0, 0, 1, 1, 1])
y_pred = np.array([0.1, 0.3, 0.6, 0.8, 0.9])
print(f"Brier score {brier_score(y_true, y_pred):.4f}")
print(f"ECE {expected_calibration_error(y_true, y_pred, n_bins=5):.4f}")
print(f"bias {mean_calibration_error(y_true, y_pred):.4f}")
brier_score is a proper scoring rule and the one to optimize.
mean_calibration_error is calibration in the large, |mean(prediction) −
base rate|. It accepts sample_weight; because opposing errors can cancel, pair
it with a proper score and calibration curve.
Reporting an honest calibration error¶
On data that is calibrated by construction the true error is zero, so whatever the plugin estimator reports is bias:
import numpy as np
from calibre import debiased_calibration_error, sweep_calibration_error
from calibre.metrics import expected_calibration_error
rng = np.random.default_rng(0)
p = rng.uniform(0, 1, 4000)
y = rng.binomial(1, p).astype(float)
print(f"plugin ECE {expected_calibration_error(y, p, n_bins=15):.4f}")
print(f"debiased {debiased_calibration_error(y, p, n_bins=15):.4f}")
print(f"sweep {sweep_calibration_error(y, p):.4f}")
Measuring what calibration cost you in resolution¶
import numpy as np
from calibre import (
CenteredIsotonicCalibrator,
IsotonicCalibrator,
unique_value_counts,
)
rng = np.random.default_rng(0)
scores = rng.uniform(0, 1, 2000)
labels = rng.binomial(1, scores).astype(float)
for name, cal in (
("isotonic", IsotonicCalibrator()),
("centered", CenteredIsotonicCalibrator()),
):
out = cal.fit(scores, labels).transform(scores)
counts = unique_value_counts(out, original_predictions=scores)
print(f"{name:9s} {counts['n_unique_predictions']:5d} distinct values")
Both are well calibrated. Only one of them still tells you which of two cases is the riskier bet.