probcal 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. probcal-0.1.0/.github/workflows/ci.yml +40 -0
  2. probcal-0.1.0/.github/workflows/docs.yml +40 -0
  3. probcal-0.1.0/.github/workflows/publish.yml +94 -0
  4. probcal-0.1.0/.gitignore +12 -0
  5. probcal-0.1.0/CHANGELOG.md +45 -0
  6. probcal-0.1.0/CITATION.cff +11 -0
  7. probcal-0.1.0/LICENSE +21 -0
  8. probcal-0.1.0/PKG-INFO +154 -0
  9. probcal-0.1.0/README.md +116 -0
  10. probcal-0.1.0/docs/DECISIONS.md +342 -0
  11. probcal-0.1.0/docs/LICENSING.md +16 -0
  12. probcal-0.1.0/docs/api/calibrators.md +15 -0
  13. probcal-0.1.0/docs/api/metrics.md +13 -0
  14. probcal-0.1.0/docs/api/tools.md +17 -0
  15. probcal-0.1.0/docs/api.md +11 -0
  16. probcal-0.1.0/docs/changelog.md +1 -0
  17. probcal-0.1.0/docs/concepts/auto-selection.md +79 -0
  18. probcal-0.1.0/docs/concepts/data-splitting.md +141 -0
  19. probcal-0.1.0/docs/concepts/img/belt.png +0 -0
  20. probcal-0.1.0/docs/concepts/img/comparison.png +0 -0
  21. probcal-0.1.0/docs/concepts/img/interval.png +0 -0
  22. probcal-0.1.0/docs/concepts/img/reliability_logit.png +0 -0
  23. probcal-0.1.0/docs/concepts/img/reliability_probability.png +0 -0
  24. probcal-0.1.0/docs/concepts/img/selection.png +0 -0
  25. probcal-0.1.0/docs/concepts/inverse-maps.md +119 -0
  26. probcal-0.1.0/docs/concepts/methods-distribution-free.md +156 -0
  27. probcal-0.1.0/docs/concepts/methods-nonparametric.md +248 -0
  28. probcal-0.1.0/docs/concepts/methods-parametric.md +240 -0
  29. probcal-0.1.0/docs/concepts/metrics.md +295 -0
  30. probcal-0.1.0/docs/concepts/offset.md +169 -0
  31. probcal-0.1.0/docs/concepts/shap-calibration.md +138 -0
  32. probcal-0.1.0/docs/concepts/visualization.md +98 -0
  33. probcal-0.1.0/docs/concepts/why-calibration.md +196 -0
  34. probcal-0.1.0/docs/faq.md +93 -0
  35. probcal-0.1.0/docs/getting-started.md +108 -0
  36. probcal-0.1.0/docs/how-it-works.md +81 -0
  37. probcal-0.1.0/docs/index.md +23 -0
  38. probcal-0.1.0/docs/javascripts/mathjax.js +16 -0
  39. probcal-0.1.0/docs/notebooks/pd_calibration_walkthrough.ipynb +453 -0
  40. probcal-0.1.0/mkdocs.yml +97 -0
  41. probcal-0.1.0/pyproject.toml +80 -0
  42. probcal-0.1.0/src/probcal/__init__.py +55 -0
  43. probcal-0.1.0/src/probcal/_math.py +715 -0
  44. probcal-0.1.0/src/probcal/_results.py +233 -0
  45. probcal-0.1.0/src/probcal/_validation.py +101 -0
  46. probcal-0.1.0/src/probcal/attribution.py +171 -0
  47. probcal-0.1.0/src/probcal/base.py +250 -0
  48. probcal-0.1.0/src/probcal/bayesian.py +255 -0
  49. probcal-0.1.0/src/probcal/binning.py +202 -0
  50. probcal-0.1.0/src/probcal/curves.py +204 -0
  51. probcal-0.1.0/src/probcal/datasets.py +97 -0
  52. probcal-0.1.0/src/probcal/isotonic.py +178 -0
  53. probcal-0.1.0/src/probcal/metrics/__init__.py +170 -0
  54. probcal-0.1.0/src/probcal/metrics/binned.py +173 -0
  55. probcal-0.1.0/src/probcal/metrics/grade.py +123 -0
  56. probcal-0.1.0/src/probcal/metrics/regression.py +103 -0
  57. probcal-0.1.0/src/probcal/metrics/scores.py +136 -0
  58. probcal-0.1.0/src/probcal/metrics/smooth.py +124 -0
  59. probcal-0.1.0/src/probcal/offset.py +211 -0
  60. probcal-0.1.0/src/probcal/parametric.py +323 -0
  61. probcal-0.1.0/src/probcal/plots.py +184 -0
  62. probcal-0.1.0/src/probcal/py.typed +0 -0
  63. probcal-0.1.0/src/probcal/selection.py +174 -0
  64. probcal-0.1.0/src/probcal/spline.py +198 -0
  65. probcal-0.1.0/src/probcal/thresholds.py +55 -0
  66. probcal-0.1.0/src/probcal/vennabers.py +165 -0
  67. probcal-0.1.0/src/probcal/wrapper.py +295 -0
  68. probcal-0.1.0/tests/test_attribution.py +132 -0
  69. probcal-0.1.0/tests/test_base.py +68 -0
  70. probcal-0.1.0/tests/test_bayesian.py +101 -0
  71. probcal-0.1.0/tests/test_binning.py +94 -0
  72. probcal-0.1.0/tests/test_curves.py +100 -0
  73. probcal-0.1.0/tests/test_datasets.py +59 -0
  74. probcal-0.1.0/tests/test_inverse_maps.py +142 -0
  75. probcal-0.1.0/tests/test_isotonic.py +101 -0
  76. probcal-0.1.0/tests/test_math.py +226 -0
  77. probcal-0.1.0/tests/test_math_reference.py +114 -0
  78. probcal-0.1.0/tests/test_metrics_binned.py +73 -0
  79. probcal-0.1.0/tests/test_metrics_evaluate.py +43 -0
  80. probcal-0.1.0/tests/test_metrics_grade.py +70 -0
  81. probcal-0.1.0/tests/test_metrics_regression.py +53 -0
  82. probcal-0.1.0/tests/test_metrics_scores.py +91 -0
  83. probcal-0.1.0/tests/test_metrics_smooth.py +62 -0
  84. probcal-0.1.0/tests/test_no_leakage.py +70 -0
  85. probcal-0.1.0/tests/test_offset.py +89 -0
  86. probcal-0.1.0/tests/test_package.py +42 -0
  87. probcal-0.1.0/tests/test_parametric.py +201 -0
  88. probcal-0.1.0/tests/test_plots.py +81 -0
  89. probcal-0.1.0/tests/test_results.py +94 -0
  90. probcal-0.1.0/tests/test_selection.py +125 -0
  91. probcal-0.1.0/tests/test_spline.py +91 -0
  92. probcal-0.1.0/tests/test_validation.py +66 -0
  93. probcal-0.1.0/tests/test_vennabers.py +105 -0
  94. probcal-0.1.0/tests/test_wrapper.py +156 -0
  95. probcal-0.1.0/uv.lock +2729 -0
@@ -0,0 +1,40 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ test:
12
+ name: Test on Python ${{ matrix.python-version }}
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ python-version: ['3.11', '3.12', '3.13']
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - name: Install uv
22
+ uses: astral-sh/setup-uv@v4
23
+
24
+ - name: Set up Python
25
+ uses: actions/setup-python@v5
26
+ with:
27
+ python-version: ${{ matrix.python-version }}
28
+
29
+ - name: Install dependencies
30
+ run: uv pip install --system -e ".[dev,viz]"
31
+
32
+ - name: Lint
33
+ run: ruff check .
34
+
35
+ - name: Type-check
36
+ if: matrix.python-version == '3.11'
37
+ run: mypy
38
+
39
+ - name: Run tests
40
+ run: pytest -q
@@ -0,0 +1,40 @@
1
+ name: Deploy Documentation
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ paths:
8
+ - 'docs/**'
9
+ - 'mkdocs.yml'
10
+ - 'src/probcal/**'
11
+ - 'CHANGELOG.md'
12
+ workflow_dispatch:
13
+
14
+ permissions:
15
+ contents: write
16
+
17
+ jobs:
18
+ deploy:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - name: Set up Python
24
+ uses: actions/setup-python@v5
25
+ with:
26
+ python-version: '3.11'
27
+
28
+ - name: Install uv
29
+ uses: astral-sh/setup-uv@v4
30
+
31
+ - name: Install dependencies
32
+ run: uv pip install --system -e ".[docs,viz]"
33
+
34
+ - name: Configure git identity
35
+ run: |
36
+ git config user.name "github-actions[bot]"
37
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
38
+
39
+ - name: Build and deploy
40
+ run: mkdocs gh-deploy --force
@@ -0,0 +1,94 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ test:
10
+ name: Test before release
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - name: Install uv
16
+ uses: astral-sh/setup-uv@v4
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.11"
22
+
23
+ - name: Install dependencies
24
+ run: uv pip install --system -e ".[dev,viz]"
25
+
26
+ - name: Run tests
27
+ run: pytest -q
28
+
29
+ build:
30
+ name: Build distribution
31
+ needs: test
32
+ runs-on: ubuntu-latest
33
+ steps:
34
+ - uses: actions/checkout@v4
35
+
36
+ - name: Install uv
37
+ uses: astral-sh/setup-uv@v4
38
+ with:
39
+ version: "latest"
40
+
41
+ - name: Set up Python
42
+ uses: actions/setup-python@v5
43
+ with:
44
+ python-version: "3.11"
45
+
46
+ - name: Verify tag matches package version
47
+ run: |
48
+ VERSION="$(uvx hatchling version)"
49
+ [ "$VERSION" = "${GITHUB_REF_NAME#v}" ] || { echo "tag ${GITHUB_REF_NAME} does not match version ${VERSION}"; exit 1; }
50
+
51
+ - name: Build package
52
+ run: uv build --sdist --wheel
53
+
54
+ - name: Upload distribution artifacts
55
+ uses: actions/upload-artifact@v4
56
+ with:
57
+ name: dist
58
+ path: dist/
59
+
60
+ publish-testpypi:
61
+ name: Publish to TestPyPI
62
+ needs: build
63
+ runs-on: ubuntu-latest
64
+ environment: testpypi
65
+ permissions:
66
+ id-token: write
67
+ steps:
68
+ - name: Download distribution artifacts
69
+ uses: actions/download-artifact@v4
70
+ with:
71
+ name: dist
72
+ path: dist/
73
+
74
+ - name: Publish to TestPyPI
75
+ uses: pypa/gh-action-pypi-publish@release/v1
76
+ with:
77
+ repository-url: https://test.pypi.org/legacy/
78
+
79
+ publish-pypi:
80
+ name: Publish to PyPI
81
+ needs: publish-testpypi
82
+ runs-on: ubuntu-latest
83
+ environment: pypi
84
+ permissions:
85
+ id-token: write
86
+ steps:
87
+ - name: Download distribution artifacts
88
+ uses: actions/download-artifact@v4
89
+ with:
90
+ name: dist
91
+ path: dist/
92
+
93
+ - name: Publish to PyPI
94
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,12 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.egg-info/
4
+ dist/
5
+ site/
6
+ .pytest_cache/
7
+ .mypy_cache/
8
+ .ruff_cache/
9
+ .cache/
10
+ .superpowers/
11
+ .claude/
12
+ .plan/
@@ -0,0 +1,45 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning: [SemVer](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2026-08-07
10
+
11
+ First public release on PyPI.
12
+
13
+ ### Fixed
14
+
15
+ - `reliability_binned`: Wilson interval bounds are now forced to contain the point estimate (floating-point noise at zero-event bins could push `ci_low` above a 0.0 event rate, breaking error-bar rendering)
16
+ - `plot_reliability(scale="logit")`: bins with an event rate of exactly 0 or 1 (no finite logit) are omitted from the point layer instead of rendering at the clipping floor and crushing the axis; they remain visible in the count margin
17
+
18
+ ### Added
19
+
20
+ - Packaging and CI: PEP 639 license metadata (SPDX `license = "MIT"` plus `license-files`, replacing the deprecated table form), Python 3.13 classifier, `Development Status :: 4 - Beta`, a tag-vs-version consistency check in the publish workflow, and a `ci.yml` matrix running lint, type-check, and the test suite on Python 3.11/3.12/3.13
21
+ - Documentation: generated example figures in the visualization chapter, "In probcal" code snippets in every concept chapter, and a probcal-vs-netcal guidance note in the FAQ
22
+
23
+ - Polish: README quickstart with real printed output, feature matrix vs scikit-learn/netcal/single-method packages, performance note on the deliberate absence of Rust acceleration, docs cross-linking pass
24
+ - Datasets, tutorial, and user docs: `make_pd_portfolio` (beta-family generative miscalibration with exact event-rate anchoring), executed tutorial notebook `pd_calibration_walkthrough.ipynb` (diagnose → select → fit → re-anchor → backtest → threshold translation, incl. the counterfactual-engine interop recipe), `getting-started.md` with a runnable quickstart and its printed output, `how-it-works.md` pipeline walkthrough with schema, three-page mkdocstrings API reference, and the FAQ (inverse-map protocol, `Target.probability` trap, selection rules)
25
+ - Automatic selection: `CalibratorSelector` — default 8-candidate menu per spec, inner stratified seeded K-fold with structurally out-of-fold-only scoring, criteria log_loss/brier/ici/smooth_ece/ece_sweep (plain ECE and Hosmer–Lemeshow refused), guardrail flags on pooled out-of-fold predictions, one-standard-error parsimony tie-break, winner refit on the full set, ranked `SelectionReport`; 9 tests incl. a spy-calibrator structural no-leakage check and the parsimony-on-calibrated-data behavior
26
+ - Wrapper flows: `CalibratedModel` — prefit flow (score + calibrate, the credit-risk canon), cv flow with duck-typed cloning (sklearn `clone` if installed, else deepcopy) and stratified seeded folds, `ensemble=False` pooled default (one calibrator on out-of-fold scores, final model refit) vs `ensemble=True` fold averaging; `offset_to` appending inspectable `LogitOffset` stages; composed `interval_inverse` and `affine_logit_coeffs_`; `predict_proba`/`predict_proba_2d`; structural no-leakage tests with a clone-surviving spy model (out-of-fold scoring disjoint from training rows, each row scored exactly once)
27
+ - Inverse maps and thresholds: `interval_inverse(lo, hi, *, space, buffer_logit)` implemented across the catalog — closed forms (Platt, temperature, `LogitOffset`), block-structure searchsorted (isotonic, CIR, histogram, scaling-binning), monotone bisection (beta, spline, Venn–Abers); `UnattainableTargetError` instead of silent clamping; non-monotone calibrators refuse with an explanatory `NotImplementedError`; `thresholds.py` with `calibrated_interval_to_raw` and the masterscale `calibrated_bands_to_raw`; 16 tests covering the full spec §10 list (round-trips, block-edge semantics, offset −δ shift, buffer monotonicity, space consistency)
28
+ - Attribution adjustment: `adjust_attributions` → `AdjustedAttribution` — affine-exact mode (exact composed Shapley values for Platt/temperature/offset via `affine_logit_coeffs_`) and Aumann–Shapley mode (exact additivity for any calibrator, central-difference fallback on degenerate rows); logit and probability scales; shap.Explanation duck-typing without a shap import; 10 tests incl. affine/AS 1e-12 equivalence and sign/rank invariance
29
+ - Logit offset: `LogitOffset` — explicit-delta and target-mean modes (unique bisection root, unit-tested against a brute-force grid), audit trail (`delta_`, `pre_mean_`, `post_mean_`, timestamp), `audit_report()` with pre/post guardrails, `interpret()` with odds-factor and central-tendency readings, affine logit coefficients for attribution composition
30
+ - Curves, belt, and plots: `curves.py` — `reliability_binned` (Wilson CIs, both scales), `reliability_loess`, `reliability_spline`, `calibration_belt` (GiViTI-style: forward LR degree selection ≤4, information-matrix pointwise bands, identity-test p-value; reimplemented from the Nattino papers); `plots.py` ([viz]-guarded) — `plot_reliability` with logit-scale probability-labeled ticks and count margin, `plot_belt`, `plot_comparison`, `plot_interval`, `plot_selection`; `SmoothReliabilityCurve` result dataclass; 11 tests incl. a hand-checked Wilson interval and belt null/alternative behavior
31
+ - Metrics catalog: `probcal.metrics` complete — proper scores (`log_loss`, `brier_score`, `brier_skill_score`, Murphy decomposition with optional bias correction, log-loss calibration/refinement split), binned estimators (`ece` l1/l2/max, `ece_debiased`, `ece_sweep`, `adaptive_ece`, `hosmer_lemeshow`), binning-free estimators (`smooth_ece` with self-consistent bandwidth, `ecce`, `ici`/`e50`/`e90`/`emax`, `spiegelhalter_z`), the recalibration-regression framework (`calibration_intercept`/`_slope`/`_test`, `calibration_guardrails`), per-grade backtests (`binomial_grade_test`, `jeffreys_grade_test` with traffic lights), and `evaluate()` with seeded bootstrap percentile CIs; 38 tests incl. hand-computed cases and sklearn/scipy references
32
+ - Spline calibrator: `SplineCalibrator` — natural cubic basis on the logit scale, penalized IRLS with second-difference roughness penalty, λ by stratified K-fold CV on log loss, effective degrees of freedom reported, post-fit monotonicity check with warning; completes the spec §6 calibrator catalog (11 of 11)
33
+ - Binning + Bayesian calibrators: `HistogramBinningCalibrator` (equal-mass/equal-width, Jeffreys shrinkage, empty-bin fallback, post-fit monotonicity flag), `ScalingBinningCalibrator` (Platt stage + equal-mass binning of fitted values), `BBQCalibrator` (Beta–Binomial marginal likelihood over a B-grid, Jeffreys prior, top-3 reporting), `ENIRCalibrator` (nearly-isotonic mPAVA path from raw data to the isotonic fit, BIC-weighted ensemble, `is_monotone_ = False`); 20 tests incl. hand-computed bin rates and the path-endpoint-equals-PAVA anchor
34
+ - Isotonic family + Venn–Abers: `IsotonicCalibrator` (PAVA step map, tie pooling, clamping, optional linear interpolation, block-structure attributes), `CenteredIsotonicCalibrator` (CIR through weight-centered block points), `VennAbersCalibrator` (IVAP with `predict_interval()`, log-loss-minimax scalarization, width reporting in `interpret()`), `CrossVennAbersCalibrator` (stratified seeded folds, geometric-mean merge, conservative envelope interval); 19 tests incl. direct-refit agreement and width-shrinkage checks
35
+ - Base API + parametric calibrators: `BaseCalibrator` (fit/predict_proba/predict_proba_2d/interpret, manual get_params/set_params, `is_monotone_`, `affine_logit_coeffs_`, `interval_inverse` contract stub), `PlattCalibrator` (Lin–Lin–Weng target smoothing), `TemperatureCalibrator` (safeguarded 1-D NLL solve), `BetaCalibrator` (variants abm/ab/a, betacal negative-coefficient refit, `constraint_active_`); public exports incl. `logit`/`expit`; 30 tests (identity/distortion recovery, monotonicity, small-sample stability, interpret contract)
36
+ - Numerical core: `_math.py` (overflow-safe logit/expit, weighted PAVA with block structure, IRLS logistic regression with ridge stabilization and separation detection, safeguarded 1-D Newton and bisection, vectorized lgamma/erf, regularized incomplete beta and lower incomplete gamma, chi-square and normal quantiles, tricube LOESS, natural cubic spline basis), `_validation.py` (score/target/weight validation with 1e-12 clipping), `_results.py` (frozen result dataclasses with `as_dict()` and aligned-table reprs); 49 unit tests plus 9 reference tests against scipy/scikit-learn/statsmodels (betainc and gammainc within 1e-12, IRLS within rtol 1e-8)
37
+ - Repository scaffold: src layout with docstring-only modules, tooling (uv, ruff, black, mypy, pytest), CI workflows (docs deploy, PyPI publish — tag-gated), documentation skeleton (mkdocs-material + MathJax), MIT license, citation metadata
38
+ - Reference verification: all 12 ⚠ references of the spec verified against primary sources; completed bibliographic records logged in `docs/DECISIONS.md` entries 10–21
39
+ - Theory guidebook, chunk 1: `concepts/why-calibration.md` (definitions, sources of miscalibration, decisioning and regulatory consequences, proper-scoring-rule lens) and `concepts/methods-parametric.md` (Platt, temperature, beta: derivations, parameter interpretation, worked reading)
40
+ - Theory guidebook, chunk 4 (completes the guidebook — ~16,400 words total): `concepts/shap-calibration.md` (identifiability obstacle, affine-exact class, Aumann–Shapley mode, invariance properties), `concepts/inverse-maps.md` (preimage identity, generalized inverses, attainability, buffer_logit drift robustness, masterscale workflow), `concepts/auto-selection.md` (selector protocol, structural no-leakage, report reading), `concepts/visualization.md` (reliability constructions, logit-scale rationale, calibration belt)
41
+ - Theory guidebook, chunk 3: `concepts/metrics.md` (full metric catalog with formulas and pathologies, bootstrap-CI protocol, report-reading order, selection-suitability table), `concepts/data-splitting.md` (prefit vs cv flows, ensemble vs pooled, calibration-set sizing, nested selection), `concepts/offset.md` (uniqueness of the bisection root, King–Zeng/Elkan/Tasche equivalences, worked re-anchoring, audit practice)
42
+ - Theory guidebook, chunk 2: `concepts/methods-nonparametric.md` (PAVA with worked micro-example, CIR, histogram binning, scaling-binning sample-complexity argument, BBQ, ENIR, spline calibration, properties table) and `concepts/methods-distribution-free.md` (IVAP construction, validity guarantee scope, scalarization caveat, exchangeability limits, CVAP geometric-mean merge)
43
+
44
+ [Unreleased]: https://github.com/wlazlod/probcal/compare/v0.1.0...HEAD
45
+ [0.1.0]: https://github.com/wlazlod/probcal/releases/tag/v0.1.0
@@ -0,0 +1,11 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite it as below."
3
+ type: software
4
+ title: "probcal: universal post-hoc probability calibration for binary classifiers"
5
+ authors:
6
+ - family-names: "Wlazło"
7
+ given-names: "Daniel"
8
+ version: 0.1.0
9
+ date-released: 2026-08-07
10
+ license: MIT
11
+ repository-code: "https://github.com/wlazlod/probcal"
probcal-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daniel Wlazło
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
probcal-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,154 @@
1
+ Metadata-Version: 2.4
2
+ Name: probcal
3
+ Version: 0.1.0
4
+ Summary: Universal post-hoc probability calibration for binary classifiers: methods, metrics, diagnostics, and auditable offsetting — numpy-only.
5
+ Project-URL: Homepage, https://github.com/wlazlod/probcal
6
+ Project-URL: Documentation, https://wlazlod.github.io/probcal/
7
+ Project-URL: Changelog, https://github.com/wlazlod/probcal/blob/main/CHANGELOG.md
8
+ Project-URL: Issues, https://github.com/wlazlod/probcal/issues
9
+ Author: Daniel Wlazło
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: binary-classification,calibration,credit-risk,isotonic-regression,probability,reliability
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: numpy>=1.26
22
+ Provides-Extra: dev
23
+ Requires-Dist: black>=24; extra == 'dev'
24
+ Requires-Dist: mypy>=1.10; extra == 'dev'
25
+ Requires-Dist: pytest>=8; extra == 'dev'
26
+ Requires-Dist: ruff>=0.4; extra == 'dev'
27
+ Requires-Dist: scikit-learn>=1.4; extra == 'dev'
28
+ Requires-Dist: scipy>=1.11; extra == 'dev'
29
+ Requires-Dist: statsmodels>=0.14; extra == 'dev'
30
+ Provides-Extra: docs
31
+ Requires-Dist: ipykernel>=6; extra == 'docs'
32
+ Requires-Dist: mkdocs-jupyter>=0.24; extra == 'docs'
33
+ Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
34
+ Requires-Dist: mkdocstrings[python]>=0.24; extra == 'docs'
35
+ Provides-Extra: viz
36
+ Requires-Dist: matplotlib>=3.8; extra == 'viz'
37
+ Description-Content-Type: text/markdown
38
+
39
+ # probcal
40
+
41
+ [![PyPI](https://img.shields.io/pypi/v/probcal.svg)](https://pypi.org/project/probcal/)
42
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
43
+ [![Python](https://img.shields.io/badge/python-3.11%2B-blue.svg)](pyproject.toml)
44
+
45
+ Universal post-hoc probability calibration for binary classifiers: methods, metrics,
46
+ diagnostics, and auditable offsetting — **numpy-only**.
47
+
48
+ `probcal` unifies the binary calibration literature (Platt, temperature, beta, isotonic,
49
+ centered isotonic, histogram binning, scaling-binning, BBQ, ENIR, Venn–Abers, spline
50
+ calibration), an extensive catalog of calibration evaluation metrics and statistical tests,
51
+ calibration visualization on both probability and logit scales, an auditable logit-offset
52
+ (central tendency) adjustment, automatic method selection under nested validation, and two
53
+ data flows (prefit and cross-validation). Primary application domain: credit-risk PD models;
54
+ the package is fully general.
55
+
56
+ **Status:** released on PyPI, beta. The API is stable enough to build on; breaking changes
57
+ bump the minor version until 1.0.
58
+
59
+ ## Installation
60
+
61
+ ```bash
62
+ pip install probcal # runtime: numpy only
63
+ pip install "probcal[viz]" # + matplotlib for probcal.plots
64
+ ```
65
+
66
+ Development setup (tests, lint, type-check):
67
+
68
+ ```bash
69
+ git clone https://github.com/wlazlod/probcal && cd probcal
70
+ uv sync --extra dev
71
+ ```
72
+
73
+ ## Quickstart
74
+
75
+ ```python
76
+ from probcal import BetaCalibrator, make_pd_portfolio
77
+ from probcal.metrics import calibration_guardrails
78
+
79
+ port = make_pd_portfolio(n=8000, random_state=42) # synthetic 3% PD portfolio
80
+
81
+ g_before = calibration_guardrails(port.y, port.scores)
82
+ print(f"before: slope={g_before.slope:.3f} intercept={g_before.intercept:+.3f} ok={g_before.all_ok}")
83
+
84
+ cal = BetaCalibrator().fit(port.scores, port.y)
85
+ p = cal.predict_proba(port.scores)
86
+
87
+ g_after = calibration_guardrails(port.y, p)
88
+ print(f"after: slope={g_after.slope:.3f} intercept={g_after.intercept:+.3f} ok={g_after.all_ok}")
89
+ print()
90
+ print(cal.interpret())
91
+ ```
92
+
93
+ Output:
94
+
95
+ ```text
96
+ before: slope=0.968 intercept=-0.765 ok=False
97
+ after: slope=1.000 intercept=+0.000 ok=True
98
+
99
+ Interpretation[BetaCalibrator]
100
+ parameter value
101
+ --------- --------
102
+ a 0.875054
103
+ b 1.58922
104
+ c -1.15227
105
+ - a = 0.875: sensitivity near s -> 0; a < 1 raises the smallest probabilities (model was overconfident in the low tail), a > 1 deepens them
106
+ - b = 1.589: sensitivity near s -> 1; the mirrored reading for the high tail
107
+ - c = -1.152: base-rate shift of -1.152 log-odds, odds factor 0.316
108
+ - identity map corresponds to (a, b, c) = (1, 1, 0)
109
+ - a != b (gap -0.714): asymmetric tail distortion that no symmetric (Platt/temperature) map could express
110
+ ```
111
+
112
+ Automatic selection, model wrapping, offsetting, and threshold translation:
113
+
114
+ ```python
115
+ from probcal import CalibratedModel, CalibratorSelector, PlattCalibrator
116
+
117
+ sel = CalibratorSelector().fit(s_cal, y_cal) # nested CV, log-loss criterion
118
+ wrapped = CalibratedModel(model, PlattCalibrator(), flow="prefit").fit(X_cal, y_cal)
119
+ wrapped.offset_to(target_mean=0.031) # auditable central-tendency stage
120
+ lo_z, hi_z = wrapped.interval_inverse(0.0, 0.02, space="logit") # "PD <= 2%" in raw margins
121
+ ```
122
+
123
+ ## Why probcal
124
+
125
+ | Capability | probcal | scikit-learn | netcal | single-method packages¹ |
126
+ |---|---|---|---|---|
127
+ | Calibration methods | 11 | 2 | many | 1 each |
128
+ | Runtime dependencies | numpy | scipy stack | torch stack | varies |
129
+ | Logit-scale diagnostics (low-PD readable) | yes | — | — | — |
130
+ | First-class auditable offset (central tendency) | yes | — | — | — |
131
+ | Automatic selection under nested validation | yes | — | — | — |
132
+ | Venn–Abers intervals | yes | — | — | venn-abers |
133
+ | Metric catalog with selection-suitability guidance | yes | partial | partial | — |
134
+ | Per-grade regulatory backtests (binomial, Jeffreys) | yes | — | — | — |
135
+ | Calibrated→raw threshold translation (`interval_inverse`) | yes | — | — | — |
136
+ | SHAP additivity repair on the calibrated scale | yes | — | — | — |
137
+ | Parameter interpretation (`interpret()`) on every method | yes | — | — | — |
138
+
139
+ ¹ betacal, venn-abers, ml-insights.
140
+
141
+ Performance note: the PAVA family and special functions are hand-rolled numpy/stdlib;
142
+ profiling on real workloads has not shown them to be a bottleneck. Rust acceleration is
143
+ deliberately out of scope unless benchmarks on production-sized portfolios say otherwise.
144
+
145
+ ## Documentation
146
+
147
+ Built with mkdocs-material; run locally with `uv run mkdocs serve`. Start with
148
+ *Getting started*, then the *Concepts* chapters — the package's theoretical foundation —
149
+ and the executed *PD calibration walkthrough* notebook.
150
+
151
+ ## License
152
+
153
+ MIT. See [LICENSE](LICENSE) and `docs/LICENSING.md` for the conceptual-reference policy on
154
+ GPL-licensed R packages.
@@ -0,0 +1,116 @@
1
+ # probcal
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/probcal.svg)](https://pypi.org/project/probcal/)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
+ [![Python](https://img.shields.io/badge/python-3.11%2B-blue.svg)](pyproject.toml)
6
+
7
+ Universal post-hoc probability calibration for binary classifiers: methods, metrics,
8
+ diagnostics, and auditable offsetting — **numpy-only**.
9
+
10
+ `probcal` unifies the binary calibration literature (Platt, temperature, beta, isotonic,
11
+ centered isotonic, histogram binning, scaling-binning, BBQ, ENIR, Venn–Abers, spline
12
+ calibration), an extensive catalog of calibration evaluation metrics and statistical tests,
13
+ calibration visualization on both probability and logit scales, an auditable logit-offset
14
+ (central tendency) adjustment, automatic method selection under nested validation, and two
15
+ data flows (prefit and cross-validation). Primary application domain: credit-risk PD models;
16
+ the package is fully general.
17
+
18
+ **Status:** released on PyPI, beta. The API is stable enough to build on; breaking changes
19
+ bump the minor version until 1.0.
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ pip install probcal # runtime: numpy only
25
+ pip install "probcal[viz]" # + matplotlib for probcal.plots
26
+ ```
27
+
28
+ Development setup (tests, lint, type-check):
29
+
30
+ ```bash
31
+ git clone https://github.com/wlazlod/probcal && cd probcal
32
+ uv sync --extra dev
33
+ ```
34
+
35
+ ## Quickstart
36
+
37
+ ```python
38
+ from probcal import BetaCalibrator, make_pd_portfolio
39
+ from probcal.metrics import calibration_guardrails
40
+
41
+ port = make_pd_portfolio(n=8000, random_state=42) # synthetic 3% PD portfolio
42
+
43
+ g_before = calibration_guardrails(port.y, port.scores)
44
+ print(f"before: slope={g_before.slope:.3f} intercept={g_before.intercept:+.3f} ok={g_before.all_ok}")
45
+
46
+ cal = BetaCalibrator().fit(port.scores, port.y)
47
+ p = cal.predict_proba(port.scores)
48
+
49
+ g_after = calibration_guardrails(port.y, p)
50
+ print(f"after: slope={g_after.slope:.3f} intercept={g_after.intercept:+.3f} ok={g_after.all_ok}")
51
+ print()
52
+ print(cal.interpret())
53
+ ```
54
+
55
+ Output:
56
+
57
+ ```text
58
+ before: slope=0.968 intercept=-0.765 ok=False
59
+ after: slope=1.000 intercept=+0.000 ok=True
60
+
61
+ Interpretation[BetaCalibrator]
62
+ parameter value
63
+ --------- --------
64
+ a 0.875054
65
+ b 1.58922
66
+ c -1.15227
67
+ - a = 0.875: sensitivity near s -> 0; a < 1 raises the smallest probabilities (model was overconfident in the low tail), a > 1 deepens them
68
+ - b = 1.589: sensitivity near s -> 1; the mirrored reading for the high tail
69
+ - c = -1.152: base-rate shift of -1.152 log-odds, odds factor 0.316
70
+ - identity map corresponds to (a, b, c) = (1, 1, 0)
71
+ - a != b (gap -0.714): asymmetric tail distortion that no symmetric (Platt/temperature) map could express
72
+ ```
73
+
74
+ Automatic selection, model wrapping, offsetting, and threshold translation:
75
+
76
+ ```python
77
+ from probcal import CalibratedModel, CalibratorSelector, PlattCalibrator
78
+
79
+ sel = CalibratorSelector().fit(s_cal, y_cal) # nested CV, log-loss criterion
80
+ wrapped = CalibratedModel(model, PlattCalibrator(), flow="prefit").fit(X_cal, y_cal)
81
+ wrapped.offset_to(target_mean=0.031) # auditable central-tendency stage
82
+ lo_z, hi_z = wrapped.interval_inverse(0.0, 0.02, space="logit") # "PD <= 2%" in raw margins
83
+ ```
84
+
85
+ ## Why probcal
86
+
87
+ | Capability | probcal | scikit-learn | netcal | single-method packages¹ |
88
+ |---|---|---|---|---|
89
+ | Calibration methods | 11 | 2 | many | 1 each |
90
+ | Runtime dependencies | numpy | scipy stack | torch stack | varies |
91
+ | Logit-scale diagnostics (low-PD readable) | yes | — | — | — |
92
+ | First-class auditable offset (central tendency) | yes | — | — | — |
93
+ | Automatic selection under nested validation | yes | — | — | — |
94
+ | Venn–Abers intervals | yes | — | — | venn-abers |
95
+ | Metric catalog with selection-suitability guidance | yes | partial | partial | — |
96
+ | Per-grade regulatory backtests (binomial, Jeffreys) | yes | — | — | — |
97
+ | Calibrated→raw threshold translation (`interval_inverse`) | yes | — | — | — |
98
+ | SHAP additivity repair on the calibrated scale | yes | — | — | — |
99
+ | Parameter interpretation (`interpret()`) on every method | yes | — | — | — |
100
+
101
+ ¹ betacal, venn-abers, ml-insights.
102
+
103
+ Performance note: the PAVA family and special functions are hand-rolled numpy/stdlib;
104
+ profiling on real workloads has not shown them to be a bottleneck. Rust acceleration is
105
+ deliberately out of scope unless benchmarks on production-sized portfolios say otherwise.
106
+
107
+ ## Documentation
108
+
109
+ Built with mkdocs-material; run locally with `uv run mkdocs serve`. Start with
110
+ *Getting started*, then the *Concepts* chapters — the package's theoretical foundation —
111
+ and the executed *PD calibration walkthrough* notebook.
112
+
113
+ ## License
114
+
115
+ MIT. See [LICENSE](LICENSE) and `docs/LICENSING.md` for the conceptual-reference policy on
116
+ GPL-licensed R packages.