API: metrics and tests¶
metrics
¶
Calibration metrics and statistical tests (flat re-exports).
evaluate lives here because it aggregates across every submodule
(DECISIONS entry). Selection guidance — what may be optimized and what is
report-only — is the table in docs/concepts/metrics.md.
HosmerLemeshowResult
dataclass
¶
HosmerLemeshowResult(statistic: float, df: int, p_value: float)
Hosmer–Lemeshow chi-square test (report-only; never a selection criterion).
BinomialGradeResult
dataclass
¶
BinomialGradeResult(grades: tuple, n: ndarray, k: ndarray, pd: ndarray, p_exact: ndarray, p_normal: ndarray, light: tuple, ci_low: ndarray, ci_high: ndarray)
Exact and approximate one-sided binomial backtest per rating grade.
JeffreysGradeResult
dataclass
¶
JeffreysGradeResult(grades: tuple, n: ndarray, k: ndarray, pd: ndarray, p_value: ndarray, light: tuple, ci_low: ndarray, ci_high: ndarray)
Jeffreys-posterior backtest per rating grade (ECB IRB practice).
SkceTestResult
dataclass
¶
SkceTestResult(statistic: float, estimator: str, method: str, p_value: float, p_value_bound: float, bandwidth: float, n_boot: int | None)
One-sided SKCE calibration test (H0: calibrated; large positive rejects).
CalibrationTestResult
dataclass
¶
CalibrationTestResult(statistic: float, p_value: float, alpha: float, beta: float)
2-df likelihood-ratio test of (intercept, slope) = (0, 1) — the Cox-framed weak calibration test.
GuardrailReport
dataclass
¶
GuardrailReport(slope: float, intercept: float, spiegelhalter_p: float, slope_ok: bool, intercept_ok: bool, spiegelhalter_ok: bool, all_ok: bool)
Three-flag calibration health summary used across the package.
Thresholds are conventions, not theorems: slope within [0.9, 1.1], intercept within +/-0.1 log-odds, Spiegelhalter p above 0.05.
LogLossDecomposition
dataclass
¶
LogLossDecomposition(calibration: float, refinement: float)
Calibration/refinement split of the log loss via a plug-in recalibration curve (LOESS; DECISIONS entry).
MurphyDecomposition
dataclass
¶
MurphyDecomposition(reliability: float, resolution: float, uncertainty: float)
Binned Murphy (1973) partition of the Brier score.
reliability - resolution + uncertainty equals the Brier score exactly
when predictions are constant within bins; otherwise the identity holds
up to the within-bin variance of p (documented binning bias).
EcceResult
dataclass
¶
EcceResult(stat_max: float, stat_mean: float)
Empirical cumulative calibration error: Kolmogorov-style max and mean of the cumulative deviation over sorted predictions.
SpiegelhalterResult
dataclass
¶
SpiegelhalterResult(z: float, p_value: float)
Spiegelhalter's z test of forecast unbiasedness (two-sided).
ReliabilitySummary
dataclass
¶
ReliabilitySummary(n: int, events: int, intercept: float, slope: float, ici: float, e90: float, spiegelhalter_p: float)
Stats-box aggregate for the annotated reliability diagram.
adaptive_ece
¶
adaptive_ece(y: object, p: object, *, n_bins: int = 15, norm: str = 'l1', sample_weight: object = None) -> float
Adaptive ECE: an explicit alias for equal-mass ece (the literature
uses both names for the same estimator).
Source code in src/probcal/metrics/binned.py
122 123 124 125 126 127 128 129 130 131 132 | |
ece
¶
ece(y: object, p: object, *, n_bins: int = 15, strategy: str = 'mass', norm: str = 'l1', sample_weight: object = None) -> float
Expected calibration error; norm="max" gives the MCE.
Binning-sensitive and upward-biased in finite samples — report, never select on it (see the metrics chapter's table).
Source code in src/probcal/metrics/binned.py
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | |
ece_debiased
¶
ece_debiased(y: object, p: object, *, n_bins: int = 15, strategy: str = 'mass', sample_weight: object = None) -> float
Bias-corrected ECE: per-bin squared gaps minus the within-bin variance of the event rate, floored at zero (correction in the spirit of Bröcker 2009 / Ferro & Fricker 2012; exact estimator in the DECISIONS log).
Source code in src/probcal/metrics/binned.py
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | |
ece_sweep
¶
ece_sweep(y: object, p: object, *, norm: str = 'l1', sample_weight: object = None) -> float
Monotonic-sweep calibration error (Roelofs et al., 2022).
Uses equal-mass bins with the largest B whose bin event rates remain
monotone non-decreasing (scan 2..min(n, 100); DECISIONS entry).
Source code in src/probcal/metrics/binned.py
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | |
hosmer_lemeshow
¶
hosmer_lemeshow(y: object, p: object, *, g: int = 10, sample_weight: object = None) -> HosmerLemeshowResult
Hosmer–Lemeshow goodness-of-fit test on g equal-mass risk groups.
The statistic depends on an essentially arbitrary grouping and its power scales with n — see the metrics chapter for why this is report-only.
Source code in src/probcal/metrics/binned.py
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | |
binomial_grade_test
¶
binomial_grade_test(y: object, p: object, grades: object, *, sample_weight: object = None) -> BinomialGradeResult
Exact binomial tail test per grade: P(X >= k | n, PD).
Small p-values flag grades with more defaults than the assigned PD
supports. The exact tail uses the incomplete-beta identity
P(X >= k) = I_PD(k, n - k + 1); the normal approximation is reported
alongside. Traffic lights: green > 0.05, amber > 0.01, red <= 0.01.
ci_low/ci_high are 90% Clopper-Pearson display intervals for the
observed rate; the traffic light itself remains the one-sided exact test,
unchanged.
Source code in src/probcal/metrics/grade.py
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | |
jeffreys_grade_test
¶
jeffreys_grade_test(y: object, p: object, grades: object, *, sample_weight: object = None) -> JeffreysGradeResult
Jeffreys test per grade: posterior P(theta <= PD | k, n) under Beta(k+1/2, n-k+1/2).
One-sided and conservative by design: a small value flags a grade whose
PD is likely understated. Do not read it two-sided (a recurring
validation error — see the metrics chapter). ci_low/ci_high are
the central 90% Jeffreys posterior display intervals; the traffic light
itself remains the one-sided posterior test, unchanged.
Source code in src/probcal/metrics/grade.py
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | |
skce
¶
skce(y: object, p: object, *, estimator: str = 'uq', kernel: str = 'laplace', bandwidth: float | None = None, scale: str = 'probability', random_state: int = 42) -> float
Squared kernel calibration error (Widmann et al., 2019, Table 1).
"uq" (default) is the unbiased quadratic estimator (may be negative);
"ul" the unbiased linear O(n) estimator over seeded disjoint pairs
(random_state controls the pairing); "biased" the nonnegative
V-statistic. bandwidth=None uses the deterministic median heuristic;
scale="logit" transforms the kernel input only (the low-PD option).
Source code in src/probcal/metrics/kernel.py
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | |
skce_test
¶
skce_test(y: object, p: object, *, method: str = 'bootstrap', n_boot: int = 999, kernel: str = 'laplace', bandwidth: float | None = None, scale: str = 'probability', random_state: int = 42) -> SkceTestResult
Calibration test on the SKCE (Widmann et al., 2019, Sec. 6 / App. G).
"bootstrap" (default): quadratic statistic with Arcones–Giné centered
resampling; O(n_boot * n^2) — the more powerful choice. "asymptotic":
linear statistic, normal approximation (Corollary G.3); O(n), preferred
for n >~ 20 000, but a single random pairing can miss slope-type
miscalibration that the bootstrap test rejects (the paper's documented
power gap). p_value_bound is the distribution-free worst case.
Source code in src/probcal/metrics/kernel.py
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | |
calibration_guardrails
¶
calibration_guardrails(y: object, p: object, *, sample_weight: object = None) -> GuardrailReport
Evaluate the three guardrail flags (printed in selection reports and offset audit reports).
Source code in src/probcal/metrics/regression.py
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | |
calibration_intercept
¶
calibration_intercept(y: object, p: object, *, sample_weight: object = None) -> float
Calibration-in-the-large in log-odds: logistic intercept with the slope fixed at 1 (offset regression on logit(p)).
Source code in src/probcal/metrics/regression.py
16 17 18 19 20 21 22 | |
calibration_slope
¶
calibration_slope(y: object, p: object, *, sample_weight: object = None) -> float
Cox calibration slope: < 1 means overfitting/overconfident spread,
1 underfitting.
Source code in src/probcal/metrics/regression.py
25 26 27 28 29 30 31 32 | |
calibration_test
¶
calibration_test(y: object, p: object, *, sample_weight: object = None) -> CalibrationTestResult
Likelihood-ratio test of joint calibration (alpha, beta) = (0, 1).
Source code in src/probcal/metrics/regression.py
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | |
brier_score
¶
brier_score(y: object, p: object, *, sample_weight: object = None) -> float
Weighted mean squared error of the probability forecast (strictly proper).
Source code in src/probcal/metrics/scores.py
31 32 33 34 | |
brier_skill_score
¶
brier_skill_score(y: object, p: object, *, sample_weight: object = None) -> float
Brier skill score vs the climatology forecast p = mean(y).
Positive values beat the base rate; 0 equals it.
Source code in src/probcal/metrics/scores.py
37 38 39 40 41 42 43 44 45 46 | |
log_loss
¶
log_loss(y: object, p: object, *, sample_weight: object = None) -> float
Weighted mean negative log-likelihood (strictly proper; the default selection criterion).
Source code in src/probcal/metrics/scores.py
23 24 25 26 27 28 | |
logloss_calibration_refinement
¶
logloss_calibration_refinement(y: object, p: object, *, frac: float = 0.75, sample_weight: object = None) -> LogLossDecomposition
Split the log loss into a calibration (KL) and refinement (entropy) part.
The conditional event rate c(p) is estimated by a LOESS smoother of
the outcome on the prediction; calibration is the mean
KL(Bernoulli(c) || Bernoulli(p)) and refinement the mean entropy of
Bernoulli(c). Only as good as the plug-in estimate of c.
Source code in src/probcal/metrics/scores.py
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | |
murphy_decomposition
¶
murphy_decomposition(y: object, p: object, *, n_bins: int = 10, strategy: str = 'mass', bias_corrected: bool = False, sample_weight: object = None) -> MurphyDecomposition
Binned reliability/resolution/uncertainty split of the Brier score.
bias_corrected=True subtracts the within-bin variance of the event
rate from the squared-gap terms (within-bin variance corrections in the
manner of Ferro & Fricker, 2012); the naive plug-in otherwise. The
decomposition inherits the binning choice — see the metrics chapter.
Source code in src/probcal/metrics/scores.py
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | |
e50
¶
e50(y: object, p: object, *, frac: float = 0.75, sample_weight: object = None) -> float
Median of the |LOESS(y|p) - p| distances.
Source code in src/probcal/metrics/smooth.py
89 90 91 | |
e90
¶
e90(y: object, p: object, *, frac: float = 0.75, sample_weight: object = None) -> float
90th percentile of the |LOESS(y|p) - p| distances.
Source code in src/probcal/metrics/smooth.py
94 95 96 | |
ecce
¶
ecce(y: object, p: object, *, sample_weight: object = None) -> EcceResult
Cumulative-deviation calibration error (Arrieta-Ibarra et al., 2022).
Sort by prediction and walk the cumulative sum of weighted residuals; under calibration the walk hovers near zero, and drift localizes miscalibration without any smoothing parameter.
Source code in src/probcal/metrics/smooth.py
62 63 64 65 66 67 68 69 70 71 72 | |
emax
¶
emax(y: object, p: object, *, frac: float = 0.75, sample_weight: object = None) -> float
Maximum of the |LOESS(y|p) - p| distances.
Source code in src/probcal/metrics/smooth.py
99 100 101 | |
ici
¶
ici(y: object, p: object, *, frac: float = 0.75, sample_weight: object = None) -> float
Integrated calibration index: weighted mean |LOESS(y|p) - p| (Austin & Steyerberg, 2019). The LOESS stage itself is unweighted (DECISIONS entry).
Source code in src/probcal/metrics/smooth.py
75 76 77 78 79 80 81 | |
smooth_ece
¶
smooth_ece(y: object, p: object, *, sample_weight: object = None) -> float
Kernel-smoothed ECE with a self-consistent bandwidth (Błasiok–Nakkiran).
Residuals are smoothed with a Gaussian kernel on the logit scale (the
paper's reflected kernel is a boundary device for [0, 1]; on the
unbounded logit scale no reflection is needed — DECISIONS entry), and the
reported value is the fixed point smECE(sigma) = sigma found by
bisection.
Source code in src/probcal/metrics/smooth.py
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | |
spiegelhalter_z
¶
spiegelhalter_z(y: object, p: object, *, sample_weight: object = None) -> SpiegelhalterResult
Spiegelhalter (1986) z statistic built on the Brier score.
The numerator has expectation zero under calibration; the statistic is asymptotically standard normal. No binning, no smoothing; aggregates the whole range, so compensating regional errors can cancel.
Source code in src/probcal/metrics/smooth.py
112 113 114 115 116 117 118 119 120 121 122 123 124 | |
evaluate
¶
evaluate(y: object, p: object, *, sample_weight: object = None, n_boot: int = 1000, seed: int = 42) -> MetricReport
Full metric report with seeded bootstrap percentile confidence intervals.
| PARAMETER | DESCRIPTION |
|---|---|
y
|
Outcomes and predicted probabilities.
TYPE:
|
p
|
Outcomes and predicted probabilities.
TYPE:
|
sample_weight
|
Observation weights (resampled together with the observations).
TYPE:
|
n_boot
|
Case-resampling bootstrap replicates (percentile CIs at 2.5/97.5).
TYPE:
|
seed
|
RNG seed; results are bit-reproducible given the seed.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
MetricReport
|
Point estimates and CI bounds for the full catalog. Note the caveat from the metrics chapter: a bootstrap CI around a biased estimator (plain ECE) quantifies its variance, not its bias. |
Source code in src/probcal/metrics/__init__.py
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | |
reliability_summary
¶
reliability_summary(y: object, p: object, *, sample_weight: object = None) -> ReliabilitySummary
Assemble the annotated-reliability stats box from existing metrics.
No new math: intercept and slope from the recalibration regression, ICI
and E90 from the LOESS distance family, and Spiegelhalter's p-value.
Lives here because, like evaluate, it aggregates across submodules;
probcal.plots only formats the result.
Source code in src/probcal/metrics/__init__.py
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | |
scores
¶
Proper scoring rules: log loss, Brier score, and their decompositions.
Theory, formulas, and pathologies: docs/concepts/metrics.md.
MurphyDecomposition
dataclass
¶
MurphyDecomposition(reliability: float, resolution: float, uncertainty: float)
Binned Murphy (1973) partition of the Brier score.
reliability - resolution + uncertainty equals the Brier score exactly
when predictions are constant within bins; otherwise the identity holds
up to the within-bin variance of p (documented binning bias).
LogLossDecomposition
dataclass
¶
LogLossDecomposition(calibration: float, refinement: float)
Calibration/refinement split of the log loss via a plug-in recalibration curve (LOESS; DECISIONS entry).
log_loss
¶
log_loss(y: object, p: object, *, sample_weight: object = None) -> float
Weighted mean negative log-likelihood (strictly proper; the default selection criterion).
Source code in src/probcal/metrics/scores.py
23 24 25 26 27 28 | |
brier_score
¶
brier_score(y: object, p: object, *, sample_weight: object = None) -> float
Weighted mean squared error of the probability forecast (strictly proper).
Source code in src/probcal/metrics/scores.py
31 32 33 34 | |
brier_skill_score
¶
brier_skill_score(y: object, p: object, *, sample_weight: object = None) -> float
Brier skill score vs the climatology forecast p = mean(y).
Positive values beat the base rate; 0 equals it.
Source code in src/probcal/metrics/scores.py
37 38 39 40 41 42 43 44 45 46 | |
murphy_decomposition
¶
murphy_decomposition(y: object, p: object, *, n_bins: int = 10, strategy: str = 'mass', bias_corrected: bool = False, sample_weight: object = None) -> MurphyDecomposition
Binned reliability/resolution/uncertainty split of the Brier score.
bias_corrected=True subtracts the within-bin variance of the event
rate from the squared-gap terms (within-bin variance corrections in the
manner of Ferro & Fricker, 2012); the naive plug-in otherwise. The
decomposition inherits the binning choice — see the metrics chapter.
Source code in src/probcal/metrics/scores.py
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | |
logloss_calibration_refinement
¶
logloss_calibration_refinement(y: object, p: object, *, frac: float = 0.75, sample_weight: object = None) -> LogLossDecomposition
Split the log loss into a calibration (KL) and refinement (entropy) part.
The conditional event rate c(p) is estimated by a LOESS smoother of
the outcome on the prediction; calibration is the mean
KL(Bernoulli(c) || Bernoulli(p)) and refinement the mean entropy of
Bernoulli(c). Only as good as the plug-in estimate of c.
Source code in src/probcal/metrics/scores.py
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | |
binned
¶
Binned calibration-error estimators: ECE family, MCE, Hosmer–Lemeshow.
Pathologies (binning sensitivity, finite-sample bias, HL power issues) are
documented in docs/concepts/metrics.md. None of these are selection
criteria; the Hosmer–Lemeshow test is report-only.
HosmerLemeshowResult
dataclass
¶
HosmerLemeshowResult(statistic: float, df: int, p_value: float)
Hosmer–Lemeshow chi-square test (report-only; never a selection criterion).
ece
¶
ece(y: object, p: object, *, n_bins: int = 15, strategy: str = 'mass', norm: str = 'l1', sample_weight: object = None) -> float
Expected calibration error; norm="max" gives the MCE.
Binning-sensitive and upward-biased in finite samples — report, never select on it (see the metrics chapter's table).
Source code in src/probcal/metrics/binned.py
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | |
ece_debiased
¶
ece_debiased(y: object, p: object, *, n_bins: int = 15, strategy: str = 'mass', sample_weight: object = None) -> float
Bias-corrected ECE: per-bin squared gaps minus the within-bin variance of the event rate, floored at zero (correction in the spirit of Bröcker 2009 / Ferro & Fricker 2012; exact estimator in the DECISIONS log).
Source code in src/probcal/metrics/binned.py
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | |
ece_sweep
¶
ece_sweep(y: object, p: object, *, norm: str = 'l1', sample_weight: object = None) -> float
Monotonic-sweep calibration error (Roelofs et al., 2022).
Uses equal-mass bins with the largest B whose bin event rates remain
monotone non-decreasing (scan 2..min(n, 100); DECISIONS entry).
Source code in src/probcal/metrics/binned.py
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | |
adaptive_ece
¶
adaptive_ece(y: object, p: object, *, n_bins: int = 15, norm: str = 'l1', sample_weight: object = None) -> float
Adaptive ECE: an explicit alias for equal-mass ece (the literature
uses both names for the same estimator).
Source code in src/probcal/metrics/binned.py
122 123 124 125 126 127 128 129 130 131 132 | |
hosmer_lemeshow
¶
hosmer_lemeshow(y: object, p: object, *, g: int = 10, sample_weight: object = None) -> HosmerLemeshowResult
Hosmer–Lemeshow goodness-of-fit test on g equal-mass risk groups.
The statistic depends on an essentially arbitrary grouping and its power scales with n — see the metrics chapter for why this is report-only.
Source code in src/probcal/metrics/binned.py
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | |
smooth
¶
Binning-free calibration metrics: smoothECE, ECCE, ICI family, Spiegelhalter z.
Theory: docs/concepts/metrics.md.
EcceResult
dataclass
¶
EcceResult(stat_max: float, stat_mean: float)
Empirical cumulative calibration error: Kolmogorov-style max and mean of the cumulative deviation over sorted predictions.
SpiegelhalterResult
dataclass
¶
SpiegelhalterResult(z: float, p_value: float)
Spiegelhalter's z test of forecast unbiasedness (two-sided).
smooth_ece
¶
smooth_ece(y: object, p: object, *, sample_weight: object = None) -> float
Kernel-smoothed ECE with a self-consistent bandwidth (Błasiok–Nakkiran).
Residuals are smoothed with a Gaussian kernel on the logit scale (the
paper's reflected kernel is a boundary device for [0, 1]; on the
unbounded logit scale no reflection is needed — DECISIONS entry), and the
reported value is the fixed point smECE(sigma) = sigma found by
bisection.
Source code in src/probcal/metrics/smooth.py
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | |
ecce
¶
ecce(y: object, p: object, *, sample_weight: object = None) -> EcceResult
Cumulative-deviation calibration error (Arrieta-Ibarra et al., 2022).
Sort by prediction and walk the cumulative sum of weighted residuals; under calibration the walk hovers near zero, and drift localizes miscalibration without any smoothing parameter.
Source code in src/probcal/metrics/smooth.py
62 63 64 65 66 67 68 69 70 71 72 | |
ici
¶
ici(y: object, p: object, *, frac: float = 0.75, sample_weight: object = None) -> float
Integrated calibration index: weighted mean |LOESS(y|p) - p| (Austin & Steyerberg, 2019). The LOESS stage itself is unweighted (DECISIONS entry).
Source code in src/probcal/metrics/smooth.py
75 76 77 78 79 80 81 | |
e50
¶
e50(y: object, p: object, *, frac: float = 0.75, sample_weight: object = None) -> float
Median of the |LOESS(y|p) - p| distances.
Source code in src/probcal/metrics/smooth.py
89 90 91 | |
e90
¶
e90(y: object, p: object, *, frac: float = 0.75, sample_weight: object = None) -> float
90th percentile of the |LOESS(y|p) - p| distances.
Source code in src/probcal/metrics/smooth.py
94 95 96 | |
emax
¶
emax(y: object, p: object, *, frac: float = 0.75, sample_weight: object = None) -> float
Maximum of the |LOESS(y|p) - p| distances.
Source code in src/probcal/metrics/smooth.py
99 100 101 | |
spiegelhalter_z
¶
spiegelhalter_z(y: object, p: object, *, sample_weight: object = None) -> SpiegelhalterResult
Spiegelhalter (1986) z statistic built on the Brier score.
The numerator has expectation zero under calibration; the statistic is asymptotically standard normal. No binning, no smoothing; aggregates the whole range, so compensating regional errors can cancel.
Source code in src/probcal/metrics/smooth.py
112 113 114 115 116 117 118 119 120 121 122 123 124 | |
regression
¶
Recalibration-regression framework: calibration intercept, slope, and joint test.
The Cox (1958) framework; lineage through Miller, Hui & Tierney (1991).
Theory: docs/concepts/metrics.md.
CalibrationTestResult
dataclass
¶
CalibrationTestResult(statistic: float, p_value: float, alpha: float, beta: float)
2-df likelihood-ratio test of (intercept, slope) = (0, 1) — the Cox-framed weak calibration test.
GuardrailReport
dataclass
¶
GuardrailReport(slope: float, intercept: float, spiegelhalter_p: float, slope_ok: bool, intercept_ok: bool, spiegelhalter_ok: bool, all_ok: bool)
Three-flag calibration health summary used across the package.
Thresholds are conventions, not theorems: slope within [0.9, 1.1], intercept within +/-0.1 log-odds, Spiegelhalter p above 0.05.
calibration_intercept
¶
calibration_intercept(y: object, p: object, *, sample_weight: object = None) -> float
Calibration-in-the-large in log-odds: logistic intercept with the slope fixed at 1 (offset regression on logit(p)).
Source code in src/probcal/metrics/regression.py
16 17 18 19 20 21 22 | |
calibration_slope
¶
calibration_slope(y: object, p: object, *, sample_weight: object = None) -> float
Cox calibration slope: < 1 means overfitting/overconfident spread,
1 underfitting.
Source code in src/probcal/metrics/regression.py
25 26 27 28 29 30 31 32 | |
calibration_test
¶
calibration_test(y: object, p: object, *, sample_weight: object = None) -> CalibrationTestResult
Likelihood-ratio test of joint calibration (alpha, beta) = (0, 1).
Source code in src/probcal/metrics/regression.py
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | |
calibration_guardrails
¶
calibration_guardrails(y: object, p: object, *, sample_weight: object = None) -> GuardrailReport
Evaluate the three guardrail flags (printed in selection reports and offset audit reports).
Source code in src/probcal/metrics/regression.py
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | |
grade
¶
Per-grade binomial and Jeffreys backtests (credit-risk rating grades).
Supervisory context (BCBS WP14; ECB 2019 instructions): each rating grade's
realized default count is tested against its assigned PD. Theory:
docs/concepts/metrics.md.
BinomialGradeResult
dataclass
¶
BinomialGradeResult(grades: tuple, n: ndarray, k: ndarray, pd: ndarray, p_exact: ndarray, p_normal: ndarray, light: tuple, ci_low: ndarray, ci_high: ndarray)
Exact and approximate one-sided binomial backtest per rating grade.
JeffreysGradeResult
dataclass
¶
JeffreysGradeResult(grades: tuple, n: ndarray, k: ndarray, pd: ndarray, p_value: ndarray, light: tuple, ci_low: ndarray, ci_high: ndarray)
Jeffreys-posterior backtest per rating grade (ECB IRB practice).
binomial_grade_test
¶
binomial_grade_test(y: object, p: object, grades: object, *, sample_weight: object = None) -> BinomialGradeResult
Exact binomial tail test per grade: P(X >= k | n, PD).
Small p-values flag grades with more defaults than the assigned PD
supports. The exact tail uses the incomplete-beta identity
P(X >= k) = I_PD(k, n - k + 1); the normal approximation is reported
alongside. Traffic lights: green > 0.05, amber > 0.01, red <= 0.01.
ci_low/ci_high are 90% Clopper-Pearson display intervals for the
observed rate; the traffic light itself remains the one-sided exact test,
unchanged.
Source code in src/probcal/metrics/grade.py
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | |
jeffreys_grade_test
¶
jeffreys_grade_test(y: object, p: object, grades: object, *, sample_weight: object = None) -> JeffreysGradeResult
Jeffreys test per grade: posterior P(theta <= PD | k, n) under Beta(k+1/2, n-k+1/2).
One-sided and conservative by design: a small value flags a grade whose
PD is likely understated. Do not read it two-sided (a recurring
validation error — see the metrics chapter). ci_low/ci_high are
the central 90% Jeffreys posterior display intervals; the traffic light
itself remains the one-sided posterior test, unchanged.
Source code in src/probcal/metrics/grade.py
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | |
kernel
¶
Kernel calibration error (SKCE) and its calibration tests.
Theory: docs/concepts/metrics.md. Widmann, Lindsten & Zachariah (2019),
"Calibration tests in multi-class classification: A unifying framework",
NeurIPS 32 (arXiv:1910.11385).
Binary specialization: with the identity-matrix kernel construction and a
prediction represented as the 2-vector (1 - p, p), the paper's kernel term
reduces to h_ij = 2 * k(s_i, s_j) * (y_i - p_i) * (y_j - p_j) — the factor
2 keeps values comparable with the paper's framework. Residuals always stay on
the probability scale; only the kernel input s may be logit-transformed.
No sample_weight: the cited U-statistic theory (unbiasedness, the
degenerate limit, the distribution-free bounds) is stated for unweighted
i.i.d. samples. Refusing the argument is honest; improvising weighted
inference is not.
Complexity: "uq", "biased", and the bootstrap test are O(n^2) memory
and O(n_boot * n^2) time; prefer method="asymptotic" for n >~ 20 000.
SkceTestResult
dataclass
¶
SkceTestResult(statistic: float, estimator: str, method: str, p_value: float, p_value_bound: float, bandwidth: float, n_boot: int | None)
One-sided SKCE calibration test (H0: calibrated; large positive rejects).
skce
¶
skce(y: object, p: object, *, estimator: str = 'uq', kernel: str = 'laplace', bandwidth: float | None = None, scale: str = 'probability', random_state: int = 42) -> float
Squared kernel calibration error (Widmann et al., 2019, Table 1).
"uq" (default) is the unbiased quadratic estimator (may be negative);
"ul" the unbiased linear O(n) estimator over seeded disjoint pairs
(random_state controls the pairing); "biased" the nonnegative
V-statistic. bandwidth=None uses the deterministic median heuristic;
scale="logit" transforms the kernel input only (the low-PD option).
Source code in src/probcal/metrics/kernel.py
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | |
skce_test
¶
skce_test(y: object, p: object, *, method: str = 'bootstrap', n_boot: int = 999, kernel: str = 'laplace', bandwidth: float | None = None, scale: str = 'probability', random_state: int = 42) -> SkceTestResult
Calibration test on the SKCE (Widmann et al., 2019, Sec. 6 / App. G).
"bootstrap" (default): quadratic statistic with Arcones–Giné centered
resampling; O(n_boot * n^2) — the more powerful choice. "asymptotic":
linear statistic, normal approximation (Corollary G.3); O(n), preferred
for n >~ 20 000, but a single random pairing can miss slope-type
miscalibration that the bootstrap test rejects (the paper's documented
power gap). p_value_bound is the distribution-free worst case.
Source code in src/probcal/metrics/kernel.py
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | |