model-diagnostics 1.4.3__tar.gz → 1.5.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 (51) hide show
  1. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/.gitignore +1 -1
  2. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/PKG-INFO +8 -8
  3. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/README.md +7 -7
  4. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/docs/gen_ref_pages.py +1 -1
  5. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/docs/index.md +6 -6
  6. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/pyproject.toml +6 -4
  7. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/__about__.py +1 -1
  8. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/array.py +109 -37
  9. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/binning.py +36 -24
  10. model_diagnostics-1.5.0/src/model_diagnostics/_utils/identification.py +108 -0
  11. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/tests/test_array.py +195 -1
  12. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/tests/test_binning.py +65 -1
  13. model_diagnostics-1.5.0/src/model_diagnostics/_utils/tests/test_identification.py +45 -0
  14. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/calibration/__init__.py +4 -2
  15. model_diagnostics-1.4.3/src/model_diagnostics/calibration/identification.py → model_diagnostics-1.5.0/src/model_diagnostics/calibration/_compute.py +3 -107
  16. model_diagnostics-1.4.3/src/model_diagnostics/calibration/plots.py → model_diagnostics-1.5.0/src/model_diagnostics/calibration/_plots.py +2 -2
  17. model_diagnostics-1.4.3/src/model_diagnostics/calibration/tests/test_identification.py → model_diagnostics-1.5.0/src/model_diagnostics/calibration/tests/test_compute.py +2 -43
  18. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/scoring/__init__.py +2 -2
  19. model_diagnostics-1.4.3/src/model_diagnostics/scoring/plots.py → model_diagnostics-1.5.0/src/model_diagnostics/scoring/_plots.py +1 -1
  20. model_diagnostics-1.4.3/src/model_diagnostics/scoring/scoring.py → model_diagnostics-1.5.0/src/model_diagnostics/scoring/_scoring.py +1 -1
  21. model_diagnostics-1.5.0/src/model_diagnostics/xai/__init__.py +9 -0
  22. model_diagnostics-1.4.3/src/model_diagnostics/_utils/partial_dependence.py → model_diagnostics-1.5.0/src/model_diagnostics/xai/_partial_dependence.py +33 -12
  23. model_diagnostics-1.5.0/src/model_diagnostics/xai/_permutation_importance.py +291 -0
  24. model_diagnostics-1.5.0/src/model_diagnostics/xai/_plots.py +168 -0
  25. model_diagnostics-1.5.0/src/model_diagnostics/xai/tests/__init__.py +0 -0
  26. {model_diagnostics-1.4.3/src/model_diagnostics/_utils → model_diagnostics-1.5.0/src/model_diagnostics/xai}/tests/test_partial_dependence.py +31 -21
  27. model_diagnostics-1.5.0/src/model_diagnostics/xai/tests/test_permutation_importance.py +216 -0
  28. model_diagnostics-1.5.0/src/model_diagnostics/xai/tests/test_plots.py +71 -0
  29. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/.codecov.yml +0 -0
  30. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/LICENSE +0 -0
  31. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/Untitled.ipynb +0 -0
  32. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/dev_tools/print_package_versions.py +0 -0
  33. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/docs/development.md +0 -0
  34. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/docs/examples/classification.ipynb +0 -0
  35. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/docs/examples/quantile_regression.ipynb +0 -0
  36. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/docs/examples/regression_on_workers_compensation.ipynb +0 -0
  37. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/docs/javascripts/mathjax.js +0 -0
  38. /model_diagnostics-1.4.3/mkdocs.yml → /model_diagnostics-1.5.0/properdocs.yml +0 -0
  39. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/__init__.py +0 -0
  40. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_config.py +0 -0
  41. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/__init__.py +0 -0
  42. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/isotonic.py +0 -0
  43. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/plot_helper.py +0 -0
  44. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/test_helper.py +0 -0
  45. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/tests/test_isotonic.py +0 -0
  46. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/calibration/tests/__init__.py +0 -0
  47. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/calibration/tests/test_plots.py +0 -0
  48. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/scoring/tests/__init__.py +0 -0
  49. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/scoring/tests/test_plots.py +0 -0
  50. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/scoring/tests/test_scoring.py +0 -0
  51. {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/tests/test_config.py +0 -0
@@ -120,7 +120,7 @@ venv.bak/
120
120
  # Rope project settings
121
121
  .ropeproject
122
122
 
123
- # mkdocs documentation
123
+ # properdocs documentation
124
124
  /site
125
125
 
126
126
  # mypy
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: model-diagnostics
3
- Version: 1.4.3
3
+ Version: 1.5.0
4
4
  Summary: Tools for diagnostics and assessment of (machine learning) models
5
5
  Project-URL: Homepage, https://lorentzenchr.github.io/model-diagnostics/
6
6
  Project-URL: Source, https://github.com/lorentzenchr/model-diagnostics
@@ -48,7 +48,7 @@ Description-Content-Type: text/markdown
48
48
 
49
49
  | | |
50
50
  | --- | --- |
51
- | CI/CD |[![CI - Test](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml/badge.svg)](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml) [![Coverage](https://codecov.io/github/lorentzenchr/model-diagnostics/coverage.svg?branch=main)](https://codecov.io/gh/lorentzenchr/model-diagnostics)
51
+ | CI/CD |[![CI - Test](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml/badge.svg)](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml) [![Coverage](https://codecov.io/github/lorentzenchr/model-diagnostics/coverage.svg?branch=main)](https://codecov.io/gh/lorentzenchr/model-diagnostics) [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/lorentzenchr/model-diagnostics/badge)](https://scorecard.dev/viewer/?uri=github.com/lorentzenchr/model-diagnostics)
52
52
  | Docs | [![Docs](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/docs.yml/badge.svg)](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/docs.yml)
53
53
  | Package | [![PyPI - Version](https://img.shields.io/pypi/v/model-diagnostics.svg?logo=pypi&label=PyPI&logoColor=gold)](https://pypi.org/project/model-diagnostics/) [![PyPI - Downloads](https://img.shields.io/pypi/dm/model-diagnostics.svg?color=blue&label=Downloads&logo=pypi&logoColor=gold)](https://pypi.org/project/model-diagnostics/) [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/model-diagnostics.svg?logo=python&label=Python&logoColor=gold)](https://pypi.org/project/model-diagnostics/) |
54
54
  | Meta | [![Hatch project](https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg)](https://github.com/pypa/hatch) [![linting - Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![types - Mypy](https://img.shields.io/badge/types-Mypy-blue.svg)](https://github.com/python/mypy) [![License - MIT](https://img.shields.io/badge/license-MIT-9400d3.svg)](https://spdx.org/licenses/)
@@ -58,14 +58,14 @@ Description-Content-Type: text/markdown
58
58
  Highlights:
59
59
 
60
60
  - All common point predictions covered: mean, median, quantiles, expectiles.
61
- - Assess model calibration with [identification functions](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/identification/#model_diagnostics.calibration.identification.identification_function) (generalized residuals), [compute_bias](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/identification/#model_diagnostics.calibration.identification.compute_bias) and [compute_marginal](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/identification/#model_diagnostics.calibration.identification.compute_marginal).
61
+ - Assess model calibration with [identification functions](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/#model_diagnostics.calibration.identification_function) (generalized residuals), [compute_bias](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/#model_diagnostics.calibration.compute_bias) and [compute_marginal](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/#model_diagnostics.calibration.compute_marginal).
62
62
  - Assess calibration and bias graphically
63
- - [reliability diagrams](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/plots/#model_diagnostics.calibration.plots.plot_reliability_diagram) for auto-calibration
64
- - [bias plots](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/plots/#model_diagnostics.calibration.plots.plot_bias) for conditional calibration
65
- - [marginal plots](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/plots/#model_diagnostics.calibration.plots.plot_marginal) for average `y_obs`, `y_pred` and partial dependence for one feature
63
+ - [reliability diagrams](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/#model_diagnostics.calibration.plot_reliability_diagram) for auto-calibration
64
+ - [bias plots](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/#model_diagnostics.calibration.plot_bias) for conditional calibration
65
+ - [marginal plots](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/#model_diagnostics.calibration.plot_marginal) for average `y_obs`, `y_pred` and partial dependence for one feature
66
66
  - Assess the predictive performance of models
67
- - strictly consistent, homogeneous [scoring functions](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/scoring/scoring/)
68
- - [score decomposition](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/scoring/scoring/#model_diagnostics.scoring.scoring.decompose) into miscalibration, discrimination and uncertainty
67
+ - strictly consistent, homogeneous [scoring functions](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/scoring/)
68
+ - [score decomposition](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/scoring/#model_diagnostics.scoring.decompose) into miscalibration, discrimination and uncertainty
69
69
  - Choose your plot backend, either [matplotlib](https://matplotlib.org) or [plotly](https://plotly.com/python/), e.g., via [set_config](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/#model_diagnostics.set_config).
70
70
 
71
71
  :rocket: To our knowledge, this is the first python package to offer reliability diagrams for quantiles and expectiles and a score decomposition, both made available by an internal implementation of isotonic quantile/expectile regression. :rocket:
@@ -2,7 +2,7 @@
2
2
 
3
3
  | | |
4
4
  | --- | --- |
5
- | CI/CD |[![CI - Test](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml/badge.svg)](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml) [![Coverage](https://codecov.io/github/lorentzenchr/model-diagnostics/coverage.svg?branch=main)](https://codecov.io/gh/lorentzenchr/model-diagnostics)
5
+ | CI/CD |[![CI - Test](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml/badge.svg)](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml) [![Coverage](https://codecov.io/github/lorentzenchr/model-diagnostics/coverage.svg?branch=main)](https://codecov.io/gh/lorentzenchr/model-diagnostics) [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/lorentzenchr/model-diagnostics/badge)](https://scorecard.dev/viewer/?uri=github.com/lorentzenchr/model-diagnostics)
6
6
  | Docs | [![Docs](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/docs.yml/badge.svg)](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/docs.yml)
7
7
  | Package | [![PyPI - Version](https://img.shields.io/pypi/v/model-diagnostics.svg?logo=pypi&label=PyPI&logoColor=gold)](https://pypi.org/project/model-diagnostics/) [![PyPI - Downloads](https://img.shields.io/pypi/dm/model-diagnostics.svg?color=blue&label=Downloads&logo=pypi&logoColor=gold)](https://pypi.org/project/model-diagnostics/) [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/model-diagnostics.svg?logo=python&label=Python&logoColor=gold)](https://pypi.org/project/model-diagnostics/) |
8
8
  | Meta | [![Hatch project](https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg)](https://github.com/pypa/hatch) [![linting - Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![types - Mypy](https://img.shields.io/badge/types-Mypy-blue.svg)](https://github.com/python/mypy) [![License - MIT](https://img.shields.io/badge/license-MIT-9400d3.svg)](https://spdx.org/licenses/)
@@ -12,14 +12,14 @@
12
12
  Highlights:
13
13
 
14
14
  - All common point predictions covered: mean, median, quantiles, expectiles.
15
- - Assess model calibration with [identification functions](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/identification/#model_diagnostics.calibration.identification.identification_function) (generalized residuals), [compute_bias](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/identification/#model_diagnostics.calibration.identification.compute_bias) and [compute_marginal](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/identification/#model_diagnostics.calibration.identification.compute_marginal).
15
+ - Assess model calibration with [identification functions](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/#model_diagnostics.calibration.identification_function) (generalized residuals), [compute_bias](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/#model_diagnostics.calibration.compute_bias) and [compute_marginal](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/#model_diagnostics.calibration.compute_marginal).
16
16
  - Assess calibration and bias graphically
17
- - [reliability diagrams](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/plots/#model_diagnostics.calibration.plots.plot_reliability_diagram) for auto-calibration
18
- - [bias plots](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/plots/#model_diagnostics.calibration.plots.plot_bias) for conditional calibration
19
- - [marginal plots](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/plots/#model_diagnostics.calibration.plots.plot_marginal) for average `y_obs`, `y_pred` and partial dependence for one feature
17
+ - [reliability diagrams](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/#model_diagnostics.calibration.plot_reliability_diagram) for auto-calibration
18
+ - [bias plots](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/#model_diagnostics.calibration.plot_bias) for conditional calibration
19
+ - [marginal plots](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration/#model_diagnostics.calibration.plot_marginal) for average `y_obs`, `y_pred` and partial dependence for one feature
20
20
  - Assess the predictive performance of models
21
- - strictly consistent, homogeneous [scoring functions](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/scoring/scoring/)
22
- - [score decomposition](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/scoring/scoring/#model_diagnostics.scoring.scoring.decompose) into miscalibration, discrimination and uncertainty
21
+ - strictly consistent, homogeneous [scoring functions](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/scoring/)
22
+ - [score decomposition](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/scoring/#model_diagnostics.scoring.decompose) into miscalibration, discrimination and uncertainty
23
23
  - Choose your plot backend, either [matplotlib](https://matplotlib.org) or [plotly](https://plotly.com/python/), e.g., via [set_config](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/#model_diagnostics.set_config).
24
24
 
25
25
  :rocket: To our knowledge, this is the first python package to offer reliability diagrams for quantiles and expectiles and a score decomposition, both made available by an internal implementation of isotonic quantile/expectile regression. :rocket:
@@ -17,7 +17,7 @@ log_msg = ["\n\tAPI tReference:"]
17
17
  for path in sorted(
18
18
  set(src.rglob("*.py"))
19
19
  - set(src.rglob("*tests/*.py"))
20
- # - set(src.rglob("*/_[!_]*.py")) # No single underscore file
20
+ - set(src.rglob("*/_[!_]*.py")) # No single underscore file
21
21
  - set(src.rglob("*/_[!_]*/*.py")) # No single underscore module
22
22
  - set(src.rglob("__about__.py"))
23
23
  ):
@@ -2,7 +2,7 @@
2
2
 
3
3
  | | |
4
4
  | --- | --- |
5
- | CI/CD |[![CI - Test](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml/badge.svg)](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml) [![Coverage](https://codecov.io/github/lorentzenchr/model-diagnostics/coverage.svg?branch=main)](https://codecov.io/gh/lorentzenchr/model-diagnostics)
5
+ | CI/CD |[![CI - Test](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml/badge.svg)](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml) [![Coverage](https://codecov.io/github/lorentzenchr/model-diagnostics/coverage.svg?branch=main)](https://codecov.io/gh/lorentzenchr/model-diagnostics) [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/lorentzenchr/model-diagnostics/badge)](https://scorecard.dev/viewer/?uri=github.com/lorentzenchr/model-diagnostics)
6
6
  | Docs | [![Docs](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/docs.yml/badge.svg)](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/docs.yml)
7
7
  | Package | [![PyPI - Version](https://img.shields.io/pypi/v/model-diagnostics.svg?logo=pypi&label=PyPI&logoColor=gold)](https://pypi.org/project/model-diagnostics/) [![PyPI - Downloads](https://img.shields.io/pypi/dm/model-diagnostics.svg?color=blue&label=Downloads&logo=pypi&logoColor=gold)](https://pypi.org/project/model-diagnostics/) [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/model-diagnostics.svg?logo=python&label=Python&logoColor=gold)](https://pypi.org/project/model-diagnostics/) |
8
8
  | Meta | [![Hatch project](https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg)](https://github.com/pypa/hatch) [![linting - Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![types - Mypy](https://img.shields.io/badge/types-Mypy-blue.svg)](https://github.com/python/mypy) [![License - MIT](https://img.shields.io/badge/license-MIT-9400d3.svg)](https://spdx.org/licenses/)
@@ -12,13 +12,13 @@
12
12
  Highlights:
13
13
 
14
14
  - All common point predictions covered: mean, median, quantiles, expectiles.
15
- - Assess model calibration with [identification functions][model_diagnostics.calibration.identification.identification_function] (generalized residuals), [compute_bias][model_diagnostics.calibration.identification.compute_bias] and [compute_marginal][model_diagnostics.calibration.identification.compute_marginal].
15
+ - Assess model calibration with [identification functions][model_diagnostics.calibration.identification_function] (generalized residuals), [compute_bias][model_diagnostics.calibration.compute_bias] and [compute_marginal][model_diagnostics.calibration.compute_marginal].
16
16
  - Assess calibration and bias graphically
17
- - [reliability diagrams][model_diagnostics.calibration.plots.plot_reliability_diagram] for auto-calibration
18
- - [bias plots][model_diagnostics.calibration.plots.plot_bias] for conditional calibration
19
- - [marginal plots][model_diagnostics.calibration.plots.plot_marginal] for average `y_obs`, `y_pred` and partial dependence for one feature
17
+ - [reliability diagrams][model_diagnostics.calibration.plot_reliability_diagram] for auto-calibration
18
+ - [bias plots][model_diagnostics.calibration.plot_bias] for conditional calibration
19
+ - [marginal plots][model_diagnostics.calibration.plot_marginal] for average `y_obs`, `y_pred` and partial dependence for one feature
20
20
  - Assess the predictive performance of models
21
- - strictly consistent, homogeneous [scoring functions][model_diagnostics.scoring.scoring]
21
+ - strictly consistent, homogeneous [scoring functions][model_diagnostics.scoring]
22
22
  - [score decomposition][model_diagnostics.scoring.decompose] into miscalibration, discrimination and uncertainty
23
23
  - Choose your plot backend, either [matplotlib](https://matplotlib.org) or [plotly](https://plotly.com/python/), e.g., via [set_config][model_diagnostics.set_config].
24
24
 
@@ -166,7 +166,8 @@ no-cov = "cov --no-cov"
166
166
 
167
167
  [tool.hatch.envs.docs]
168
168
  dependencies = [
169
- "mkdocs-material~=9.5.31",
169
+ "properdocs>=1.6.7", # https://github.com/ProperDocs/properdocs/discussions/33
170
+ "mkdocs-material~=9.6.7",
170
171
  "mkdocstrings[python]>=0.25.1",
171
172
  "mkdocs-gen-files>=0.5.0",
172
173
  "mkdocs-literate-nav>=0.6.1",
@@ -176,9 +177,9 @@ dependencies = [
176
177
  ]
177
178
 
178
179
  [tool.hatch.envs.docs.scripts]
179
- build = "mkdocs build --clean --strict {args}"
180
- serve = "mkdocs serve --dev-addr localhost:8000 {args}"
181
- ci-build = "mkdocs gh-deploy --force {args}"
180
+ build = "properdocs build --clean --strict {args}"
181
+ serve = "properdocs serve --dev-addr localhost:8000 {args}"
182
+ ci-build = "properdocs gh-deploy --force {args}"
182
183
 
183
184
  [tool.hatch.envs.jupyter]
184
185
  extra-dependencies = [
@@ -297,4 +298,5 @@ doctest_optionflags = [
297
298
  filterwarnings = [
298
299
  # fail on any warnings that are not explicitly matched below
299
300
  "error",
301
+ "ignore:Support for the dataframe interchange protocol:DeprecationWarning",
300
302
  ]
@@ -14,4 +14,4 @@
14
14
  # Dev branch marker is: 'X.Y.dev' or 'X.Y.devN' where N is an integer.
15
15
  # 'X.Y.dev0' is the canonical version of 'X.Y.dev'
16
16
  #
17
- __version__ = "1.4.3"
17
+ __version__ = "1.5.0"
@@ -1,13 +1,10 @@
1
1
  import copy
2
2
  import sys
3
- import warnings
4
- from importlib.metadata import version
5
3
  from typing import Optional, Union
6
4
 
7
5
  import numpy as np
8
6
  import numpy.typing as npt
9
7
  import polars as pl
10
- from packaging.version import Version, parse
11
8
 
12
9
  AL_or_polars = Union[npt.ArrayLike, pl.Series]
13
10
 
@@ -261,46 +258,32 @@ def safe_assign_column(x, values, column_index):
261
258
  # Setting an item of incompatible dtype is deprecated and will raise in a
262
259
  # future error of pandas.
263
260
  # Also, assigning with a different index makes troubles.
261
+ # Also, we avoid to have pyarrow installed, which newer versions of polars
262
+ # need to series.to_pandas().
264
263
  pd = sys.modules["pandas"]
265
264
  dtype = x.dtypes.iloc[column_index]
266
- if isinstance(values, pl.Series) and isinstance(
267
- values.dtype, pl.Categorical
268
- ):
269
- # FIXME: pyarrow not installed
270
- pd_values = pd.Series(
271
- data=values.cast(pl.Utf8).to_numpy(),
272
- dtype=dtype,
273
- )
265
+ if isinstance(values, pl.Series):
266
+ if isinstance(values.dtype, (pl.Categorical, pl.String)):
267
+ pd_values = pd.Series(
268
+ data=values.cast(pl.String).to_numpy(),
269
+ dtype=dtype,
270
+ )
271
+ elif values.dtype.is_integer() or values.dtype.is_float():
272
+ pd_values = pd.Series(
273
+ data=values.to_numpy(),
274
+ dtype=dtype,
275
+ )
276
+ else:
277
+ pd_values = pd.Series(
278
+ data=values.to_pandas(),
279
+ dtype=dtype,
280
+ )
274
281
  else:
275
282
  pd_values = pd.Series(
276
- data=values.to_pandas()
277
- if isinstance(values, pl.Series)
278
- else values,
283
+ data=values,
279
284
  dtype=dtype,
280
285
  )
281
- if parse(version("pandas")) < Version("2.0.0"):
282
- # FIXME: pandas >= 2.0 (<2.0 means 1.5.*)
283
- with warnings.catch_warnings():
284
- msg = (
285
- r"In a future version, `df.iloc\[:, i\] = newvals` will "
286
- r"attempt to set the values inplace instead of always "
287
- r"setting a new array. To retain the old behavior, use either "
288
- r"`df\[df.columns\[i\]\] = newvals` or, if columns are "
289
- r"non-unique, `df.isetitem\(i, newvals\)`"
290
- )
291
- warnings.filterwarnings(
292
- "ignore", category=DeprecationWarning, message=msg
293
- )
294
- if not x.index.is_unique:
295
- # Pandas might error with:
296
- # cannot reindex on an axis with duplicate labels
297
- # Try reindexing ourselves.
298
- x = x.reset_index()
299
- if not pd_values.index.is_unique:
300
- pd_values = pd_values.reset_index()
301
- x.iloc[:, column_index] = pd_values
302
- else:
303
- x.iloc[:, column_index] = pd_values
286
+ x.iloc[:, column_index] = pd_values
304
287
  except Exception as e:
305
288
  # FIXME: pyarrow version XXX
306
289
  # Older pyarrow versions of AttributeError do not have a 'add_note' method.
@@ -352,3 +335,92 @@ def safe_index_rows(x, indices):
352
335
  else:
353
336
  # numpy, polars
354
337
  return x[indices]
338
+
339
+
340
+ def safe_copy(x) -> npt.ArrayLike:
341
+ """Create a safe copy of input data in various formats.
342
+
343
+ 'Safe' means that it is guaranteed that the original data will not be modified
344
+ by modifications of the 'safe' copy. But only copy if really required.
345
+
346
+ Parameters
347
+ ----------
348
+ x : array-like
349
+ The input data to be copied, which can be numpy array, pandas DataFrame,
350
+ polars DataFrame, PyArrow Table, or other object types.
351
+
352
+ Returns
353
+ -------
354
+ copied_x : array-like
355
+ A copy of the input data in the same format.
356
+ """
357
+ if is_pyarrow_table(x) or isinstance(x, pl.DataFrame):
358
+ # Copy on Write
359
+ pass
360
+ elif hasattr(x, "copy"):
361
+ # list, numpy, pandas, scipy sparse, ...
362
+ x = x.copy()
363
+ elif not isinstance(x, tuple):
364
+ x = copy.deepcopy(x)
365
+ return x
366
+
367
+
368
+ def get_column_names(x) -> list:
369
+ """Extract column names from different data containers.
370
+
371
+ This function handles different data container formats and returns
372
+ their column names or indices if names are not available.
373
+
374
+ Parameters
375
+ ----------
376
+ x : array-like
377
+ The input data which can be a numpy array, pandas DataFrame,
378
+ polars DataFrame, PyArrow Table, or other similar data container.
379
+
380
+ Returns
381
+ -------
382
+ list
383
+ A list of column names if available, or column indices (integers)
384
+ for array-like objects without named columns.
385
+ """
386
+ if is_pyarrow_table(x):
387
+ colnames = x.column_names
388
+ elif is_pandas_df(x):
389
+ colnames = x.columns.to_list()
390
+ elif hasattr(x, "columns") and isinstance(x.columns, list):
391
+ # polars
392
+ colnames = x.columns
393
+ else:
394
+ # e.g. numpy
395
+ colnames = [str(i) for i in range(x.shape[1])]
396
+ return colnames
397
+
398
+
399
+ def to_pl_series(x, name=None) -> pl.Series:
400
+ """Convert x to a polars Series."""
401
+ try:
402
+ feature_out = pl.Series(name=name, values=x)
403
+ except ImportError:
404
+ # FIXME: pyarrow not installed
405
+ # For non numpy-backed columns, pyarrow is needed. Here we handle the case
406
+ # where pyarrow is not installed and such a pandas extention array is
407
+ # passed, e.g. with CategoricalDtype.
408
+ if is_pandas_series(x):
409
+ pandas = sys.modules["pandas"]
410
+ is_pandas_categorical = isinstance(
411
+ x.dtype, # type: ignore
412
+ pandas.CategoricalDtype,
413
+ )
414
+ feature_out = pl.from_dataframe(
415
+ x.to_frame(name=name) # type: ignore
416
+ )[:, 0]
417
+ if name is None:
418
+ feature_out = feature_out.alias("")
419
+ if is_pandas_categorical and isinstance(feature_out.dtype, pl.Enum):
420
+ # Pandas categoricals usually get mapped to polars categoricals.
421
+ # But this code path gives pl.Enum.
422
+ feature_out = feature_out.cast(pl.Categorical)
423
+ else:
424
+ raise # re-raises the ImportError
425
+
426
+ return feature_out
@@ -1,4 +1,4 @@
1
- import sys
1
+ from math import ceil
2
2
  from typing import Optional, Union
3
3
 
4
4
  import numpy as np
@@ -7,8 +7,8 @@ import polars as pl
7
7
 
8
8
  from model_diagnostics._utils.array import (
9
9
  array_name,
10
- is_pandas_series,
11
10
  length_of_first_dimension,
11
+ to_pl_series,
12
12
  )
13
13
 
14
14
 
@@ -118,34 +118,15 @@ def bin_feature(
118
118
  feature_name = array_name(feature, default=default)
119
119
  # The following statement, i.e. possibly the creation of a pl.Categorical,
120
120
  # MUST be under the StringCache context manager!
121
- try:
122
- feature_out = pl.Series(name=feature_name, values=feature)
123
- except ImportError:
124
- # FIXME: pyarrow not installed
125
- # For non numpy-backed columns, pyarrow is needed. Here we handle the case
126
- # where pyarrow is not installed and such a pandas extention array is
127
- # passed, e.g. with CategoricalDtype.
128
- if is_pandas_series(feature):
129
- pandas = sys.modules["pandas"]
130
- is_pandas_categorical = isinstance(
131
- feature.dtype, # type: ignore
132
- pandas.CategoricalDtype,
133
- )
134
- feature_out = pl.from_dataframe(
135
- feature.to_frame(name=feature_name) # type: ignore
136
- )[:, 0]
137
- if is_pandas_categorical and isinstance(feature_out.dtype, pl.Enum):
138
- # Pandas categoricals usually get mapped to polars categoricals.
139
- # But this code path gives pl.Enum.
140
- feature_out = feature_out.cast(pl.Categorical)
141
- else:
142
- raise # re-raises the ImportError
121
+ feature_out = to_pl_series(feature, name=feature_name)
122
+
143
123
  if length_of_first_dimension(feature_out) != n_obs:
144
124
  msg = (
145
125
  f"The feature array {feature_name} does not have length {n_obs} of its"
146
126
  " first dimension."
147
127
  )
148
128
  raise ValueError(msg)
129
+
149
130
  if feature_out.dtype == pl.Categorical:
150
131
  is_categorical = True
151
132
  elif feature_out.dtype == pl.Enum:
@@ -299,3 +280,34 @@ def bin_feature(
299
280
  .collect()
300
281
  )
301
282
  return feature_out, n_bins_ef + feature_out.has_nulls(), f_binned
283
+
284
+
285
+ def compute_grid(x: pl.Series, n: int) -> pl.Series:
286
+ """Compute a grid with n equal distributed points.
287
+
288
+ Best call this function inside a `with pl.StringCache()` context manager.
289
+ """
290
+ if x.dtype.is_numeric() or x.dtype.is_temporal():
291
+ f_min, f_max = x.min(), x.max()
292
+ if f_min == f_max:
293
+ return pl.Series([f_min], dtype=x.dtype)
294
+ elif x.dtype.is_float():
295
+ # pl.linear_space was added in version 1.21.0, we have minimum 1.1.0.
296
+ grid = pl.Series(np.linspace(f_min, f_max, n), dtype=x.dtype)
297
+ elif x.dtype.is_integer():
298
+ step = max(1, ceil((f_max - f_min + 1) / n))
299
+ grid = pl.int_range(f_min, f_max + 1, step, dtype=x.dtype, eager=True)
300
+ elif isinstance(x.dtype, (pl.Date, pl.Datetime, pl.Time)):
301
+ d_range = {
302
+ pl.Date: pl.date_range,
303
+ pl.Datetime: pl.datetime_range,
304
+ pl.Time: pl.time_range,
305
+ }
306
+ n_res = int((f_max - f_min) / f_min.resolution) // max(1, (n - 1))
307
+ grid = d_range[x.dtype](f_min, f_max, n_res * f_min.resolution, eager=True)
308
+ # TODO: pl.Duration
309
+ else:
310
+ # String, Categorical, Enum or complex types such as List, Union
311
+ grid = x.value_counts(sort=True).head(n)[:, 0]
312
+
313
+ return grid
@@ -0,0 +1,108 @@
1
+ import numpy as np
2
+ import numpy.typing as npt
3
+
4
+ from model_diagnostics._utils.array import validate_2_arrays
5
+
6
+
7
+ def identification_function(
8
+ y_obs: npt.ArrayLike,
9
+ y_pred: npt.ArrayLike,
10
+ *,
11
+ functional: str = "mean",
12
+ level: float = 0.5,
13
+ ) -> np.ndarray:
14
+ r"""Canonical identification function.
15
+
16
+ Identification functions act as generalised residuals. See [Notes](#notes) for
17
+ further details.
18
+
19
+ Parameters
20
+ ----------
21
+ y_obs : array-like of shape (n_obs)
22
+ Observed values of the response variable.
23
+ For binary classification, y_obs is expected to be in the interval [0, 1].
24
+ y_pred : array-like of shape (n_obs)
25
+ Predicted values of the `functional`, e.g. the conditional expectation of
26
+ the response, `E(Y|X)`.
27
+ functional : str
28
+ The functional that is induced by the identification function `V`. Options are:
29
+
30
+ - `"mean"`. Argument `level` is neglected.
31
+ - `"median"`. Argument `level` is neglected.
32
+ - `"expectile"`
33
+ - `"quantile"`
34
+
35
+ level : float
36
+ The level of the expectile of quantile. (Often called \(\alpha\).)
37
+ It must be `0 < level < 1`.
38
+ `level=0.5` and `functional="expectile"` gives the mean.
39
+ `level=0.5` and `functional="quantile"` gives the median.
40
+
41
+ Returns
42
+ -------
43
+ V : ndarray of shape (n_obs)
44
+ Values of the identification function.
45
+
46
+ Notes
47
+ -----
48
+ [](){#notes}
49
+
50
+ The function \(V(y, z)\) for observation \(y=y_{pred}\) and prediction
51
+ \(z=y_{pred}\) is a strict identification function for the functional \(T\), or
52
+ induces the functional \(T\) as:
53
+
54
+ \[
55
+ \mathbb{E}[V(Y, z)] = 0\quad \Leftrightarrow\quad z\in T(F) \quad \forall
56
+ \text{ distributions } F
57
+ \in \mathcal{F}
58
+ \]
59
+
60
+ for some class of distributions \(\mathcal{F}\). Implemented examples of the
61
+ functional \(T\) are mean, median, expectiles and quantiles.
62
+
63
+ | functional | strict identification function \(V(y, z)\) |
64
+ | ---------- | ---------------------------------------------------- |
65
+ | mean | \(z - y\) |
66
+ | median | \(\mathbf{1}\{z \ge y\} - \frac{1}{2}\) |
67
+ | expectile | \(2 \mid\mathbf{1}\{z \ge y\} - \alpha\mid (z - y)\) |
68
+ | quantile | \(\mathbf{1}\{z \ge y\} - \alpha\) |
69
+
70
+ For `level` \(\alpha\).
71
+
72
+ References
73
+ ----------
74
+ `[Gneiting2011]`
75
+
76
+ : T. Gneiting.
77
+ "Making and Evaluating Point Forecasts". (2011)
78
+ [doi:10.1198/jasa.2011.r10138](https://doi.org/10.1198/jasa.2011.r10138)
79
+ [arxiv:0912.0902](https://arxiv.org/abs/0912.0902)
80
+
81
+ Examples
82
+ --------
83
+ >>> identification_function(y_obs=[0, 0, 1, 1], y_pred=[-1, 1, 1 , 2])
84
+ array([-1, 1, 0, 1])
85
+ """
86
+ y_o: np.ndarray
87
+ y_p: np.ndarray
88
+ y_o, y_p = validate_2_arrays(y_obs, y_pred)
89
+
90
+ if functional in ("expectile", "quantile") and (level <= 0 or level >= 1):
91
+ msg = f"Argument level must fulfil 0 < level < 1, got {level}."
92
+ raise ValueError(msg)
93
+
94
+ if functional == "mean":
95
+ return y_p - y_o
96
+ elif functional == "median":
97
+ return np.greater_equal(y_p, y_o) - 0.5
98
+ elif functional == "expectile":
99
+ return 2 * np.abs(np.greater_equal(y_p, y_o) - level) * (y_p - y_o)
100
+ elif functional == "quantile":
101
+ return np.greater_equal(y_p, y_o) - level
102
+ else:
103
+ allowed_functionals = ("mean", "median", "expectile", "quantile")
104
+ msg = (
105
+ f"Argument functional must be one of {allowed_functionals}, got "
106
+ f"{functional}."
107
+ )
108
+ raise ValueError(msg)