Repository navigation
feat(metrics): add calibration diagnostics for probability forecasts - #92
Merged
Merged
Conversation
Add ml4t.diagnostic.metrics.calibration: brier_score, log_loss with clipping, brier_decomposition (Murphy reliability/resolution/uncertainty plus the Stephenson et al. within-bin terms so the identity is exact for binned continuous forecasts), reliability_table with Wilson intervals or a seeded cluster bootstrap for outcomes correlated within an event, expected/max calibration error, and calibration_slope (linear Mincer-Zarnowitz or logit Cox slope) with CR1/HC1 sandwich errors for measuring the favorite-longshot bias. Default bins are finer near 0 and 1 for prediction-market prices. Tests plant a favorite-longshot distortion and check recovery, check the decomposition identity, compare the sandwich with statsmodels, and check that the cluster bootstrap and cluster-robust errors widen for shared outcomes. Adds a Methods docs page with an executed example. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The PyPI advisory audit in the security workflow reported 19 known vulnerabilities in pypdf 6.16.2, urllib3 2.7.0 and virtualenv 21.7.1, failing every Audit job. Lock pypdf 6.19.0, urllib3 2.8.0 and virtualenv 21.14.6 (python-discovery follows virtualenv); pip-audit now reports no known vulnerabilities. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
ml4t.diagnostic.metrics.calibration, exported fromml4t.diagnostic.metrics, for scoring probability forecasts (model outputs or prediction-market prices) against binary outcomes, including outcomes correlated within an event.brier_score(forecasts, outcomes, *, weights=None)floatlog_loss(forecasts, outcomes, *, weights=None, eps=1e-15)float(forecasts clipped to[eps, 1-eps])brier_decomposition(forecasts, outcomes, *, bins=None, weights=None)BrierDecomposition: reliability, resolution, uncertainty, within-bin variance and covariance, base rate,brier_skill_scorereliability_table(forecasts, outcomes, *, bins=PREDICTION_MARKET_BIN_EDGES, clusters=None, weights=None, confidence=0.95, n_bootstrap=1000, seed=0)pl.DataFrame, one row per bin: n, n_clusters, weight, mean forecast, observed frequency, difference, CI, CI methodexpected_calibration_error(...),max_calibration_error(...)floatcalibration_slope(forecasts, outcomes, *, spec="linear"|"logit", clusters=None, weights=None, confidence=0.95, eps=1e-3)CalibrationSlope: a, b, SEs, CIs, p-value for b = 1Design choices:
bins=Noneeach distinct forecast is a bin (Murphy, 1973), which suits tick-quoted prices. With explicit bins, the two Stephenson et al. (2008) within-bin terms are added so the components still sum to the Brier score.clustersis given. The bootstrap resamples whole clusters across all bins using per-cluster sparse sums, so it scales to about 1M rows and 200k clusters with 1000 replicates in roughly 5 s. The library has no existing cluster bootstrap; its existing bootstraps are stationary/block resampling for time series.var_weights.np.linspace(0, 1, 11)still place a price of 0.3 in[0.3, 0.4).Tests
tests/metrics/test_calibration.py(35 tests) checks the following:logit q = 1.5 logit p) is recovered by the reliability table, with intervals covering the true bin frequency, and by the slope;Each of 14 planted bugs was confirmed to fail its targeted test, and every test passed again after the source was restored. The planted bugs: drop the within-bin covariance, resolution around 0.5, bootstrap over observations instead of clusters, wrong Wilson z, unweighted observed frequency, closed-left bins, no edge rounding, reversed difference sign, unweighted ECE, no log-loss clip, unaggregated cluster scores, logit spec without the transform, p-value against 0, and no small-sample factor.
Docs
New Methods page
docs/methods/calibration.md. Its example is executed bytest_documentation_examples. Also updates the nav, the methods index, the API reference members, the reference list, andmetrics/AGENTS.md.Dependency lock
A second commit updates
uv.lockto pypdf 6.19.0, urllib3 2.8.0 and virtualenv 21.14.6. The security workflow PyPI audit found 19 known vulnerabilities in the previously locked versions, which failed every Audit job and blocked the required Build Package check. These are transitive dependencies and the lock change is independent of the calibration code.pip-auditnow reports none, and the full suite still passes.Local gates
ruff check/ruff format --check: passty check: passpytest tests/ -n auto --timeout 120: 5472 passed, 7 skippedmkdocs build --strict: passcheck_documentation_links.py --site site: passcheck_book_links.py: passpre-commit run --all-files: pass🤖 Generated with Claude Code