Skip to main content

probcal

PyPI License: MIT Python

Universal post-hoc probability calibration for binary classifiers: methods, metrics, diagnostics, and auditable offsetting — numpy-only.

probcal unifies the binary calibration literature (Platt, temperature, beta, isotonic, centered isotonic, histogram binning, scaling-binning, BBQ, ENIR, Venn–Abers, spline calibration), an extensive catalog of calibration evaluation metrics and statistical tests, calibration visualization on both probability and logit scales, an auditable logit-offset (central tendency) adjustment, automatic method selection under nested validation, and two data flows (prefit and cross-validation). Primary application domain: credit-risk PD models; the package is fully general.

Status: released on PyPI, beta. The API is stable enough to build on; breaking changes bump the minor version until 1.0.

Installation

pip install probcal            # runtime: numpy only
pip install "probcal[viz]"     # + matplotlib for probcal.plots

Development setup (tests, lint, type-check):

git clone https://github.com/wlazlod/probcal && cd probcal
uv sync --extra dev

Quickstart

from probcal import BetaCalibrator, make_pd_portfolio
from probcal.metrics import calibration_guardrails

port = make_pd_portfolio(n=8000, random_state=42)   # synthetic 3% PD portfolio

g_before = calibration_guardrails(port.y, port.scores)
print(f"before: slope={g_before.slope:.3f}  intercept={g_before.intercept:+.3f}  ok={g_before.all_ok}")

cal = BetaCalibrator().fit(port.scores, port.y)
p = cal.predict_proba(port.scores)

g_after = calibration_guardrails(port.y, p)
print(f"after:  slope={g_after.slope:.3f}  intercept={g_after.intercept:+.3f}  ok={g_after.all_ok}")
print()
print(cal.interpret())

Output:

before: slope=0.968  intercept=-0.765  ok=False
after:  slope=1.000  intercept=+0.000  ok=True

Interpretation[BetaCalibrator]
parameter  value
---------  --------
a          0.875054
b          1.58922
c          -1.15227
- a = 0.875: sensitivity near s -> 0; a < 1 raises the smallest probabilities (model was overconfident in the low tail), a > 1 deepens them
- b = 1.589: sensitivity near s -> 1; the mirrored reading for the high tail
- c = -1.152: base-rate shift of -1.152 log-odds, odds factor 0.316
- identity map corresponds to (a, b, c) = (1, 1, 0)
- a != b (gap -0.714): asymmetric tail distortion that no symmetric (Platt/temperature) map could express

Automatic selection, model wrapping, offsetting, and threshold translation:

from probcal import CalibratedModel, CalibratorSelector, PlattCalibrator

sel = CalibratorSelector().fit(s_cal, y_cal)             # nested CV, log-loss criterion
wrapped = CalibratedModel(model, PlattCalibrator(), flow="prefit").fit(X_cal, y_cal)
wrapped.offset_to(target_mean=0.031)                     # auditable central-tendency stage
lo_z, hi_z = wrapped.interval_inverse(0.0, 0.02, space="logit")   # "PD <= 2%" in raw margins

Why probcal

Capability probcal scikit-learn netcal probcal (R)² single-method packages¹
Calibration methods 11 2 many 5 binary³ 1 each
Runtime dependencies numpy scipy stack torch stack native R varies
Logit-scale diagnostics (low-PD readable) yes
First-class auditable offset (central tendency) yes
Automatic selection under nested validation yes
Venn–Abers intervals yes venn-abers
Metric catalog with selection-suitability guidance yes partial partial partial
Per-grade regulatory backtests (binomial, Jeffreys) yes
Kernel calibration error and test (SKCE, Widmann et al.) yes yes
Calibrated→raw threshold translation (interval_inverse) yes
SHAP additivity repair on the calibrated scale yes
Parameter interpretation (interpret()) on every method yes partial

¹ betacal, venn-abers, ml-insights. ² prdm0/probcal (P. R. Diniz Marinho), unaffiliated — see the FAQ. Verified against v0.2.0, 2026-08-08. ³ Platt, temperature, beta, isotonic, histogram binning; its multiclass methods (Dirichlet, vector scaling, one-vs-rest) are out of probcal's binary scope.

Performance note: the ICI family (ici/e50/e90/emax) shares one LOESS fit anchored to grid_size=512 quantile points instead of refitting at every observation — the same device R's stats::lowess uses via its delta parameter (fit at spaced points, interpolate the rest) — and smooth_ece pre-aggregates its residual measure onto bins=8192 cells before the bandwidth bisection. Measured on this host: ici at n=50,000 dropped from 192.2s (v0.1.2) to 1.2s, and loess(grid_size=512) now fits n=1,000,000 points in under 30s. grid_size=None and bins=None recover the exact pre-0.1.3 values and cost, so nothing is lost for portfolios small enough to afford it. Still numpy-only; Rust acceleration remains out of scope unless a future workload demands it.

Documentation

Built with mkdocs-material; run locally with uv run mkdocs serve. Start with Getting started, then the Concepts chapters — the package's theoretical foundation — and the executed PD calibration walkthrough notebook. The Visualization chapter is a gallery of every plot, regenerated deterministically by docs/scripts/generate_figures.py.

License

MIT. See LICENSE and docs/LICENSING.md for the conceptual-reference policy on GPL-licensed R packages.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

probcal-0.1.3.tar.gz (858.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

probcal-0.1.3-py3-none-any.whl (95.5 kB view details)

Uploaded Python 3

File details

Details for the file probcal-0.1.3.tar.gz.

File metadata

  • Download URL: probcal-0.1.3.tar.gz
  • Upload date:
  • Size: 858.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for probcal-0.1.3.tar.gz
Algorithm Hash digest
SHA256 d5a5a6712360036ad550f4d349bffe150178c984c144e0059550a8eb3754c5bb
MD5 ed44121a32f608f528bb8104edf653ff
BLAKE2b-256 1d404f592fc97ef99c238b8406ae0d21f786d7c646a6ca7a6a7f33ead1bde719

See more details on using hashes here.

Provenance

The following attestation bundles were made for probcal-0.1.3.tar.gz:

Publisher: publish.yml on wlazlod/probcal

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file probcal-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: probcal-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 95.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for probcal-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 90ade6eefc88343bc8d0c14bcbd4d4e8ff062d8189bde490c8f3f421373d82af
MD5 23b4aa850b46b5db4086b6b58017b021
BLAKE2b-256 cb9be855ce6c8ea7ba6e99470ccf7a4e752bdee87a423f964f5a6f5a5d4e03b7

See more details on using hashes here.

Provenance

The following attestation bundles were made for probcal-0.1.3-py3-none-any.whl:

Publisher: publish.yml on wlazlod/probcal

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.3.0

2 files

0.2.0

2 files

This release

0.1.3 This release

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page