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.
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/.gitignore +1 -1
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/PKG-INFO +8 -8
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/README.md +7 -7
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/docs/gen_ref_pages.py +1 -1
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/docs/index.md +6 -6
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/pyproject.toml +6 -4
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/__about__.py +1 -1
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/array.py +109 -37
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/binning.py +36 -24
- model_diagnostics-1.5.0/src/model_diagnostics/_utils/identification.py +108 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/tests/test_array.py +195 -1
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/tests/test_binning.py +65 -1
- model_diagnostics-1.5.0/src/model_diagnostics/_utils/tests/test_identification.py +45 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/calibration/__init__.py +4 -2
- model_diagnostics-1.4.3/src/model_diagnostics/calibration/identification.py → model_diagnostics-1.5.0/src/model_diagnostics/calibration/_compute.py +3 -107
- model_diagnostics-1.4.3/src/model_diagnostics/calibration/plots.py → model_diagnostics-1.5.0/src/model_diagnostics/calibration/_plots.py +2 -2
- 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
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/scoring/__init__.py +2 -2
- model_diagnostics-1.4.3/src/model_diagnostics/scoring/plots.py → model_diagnostics-1.5.0/src/model_diagnostics/scoring/_plots.py +1 -1
- model_diagnostics-1.4.3/src/model_diagnostics/scoring/scoring.py → model_diagnostics-1.5.0/src/model_diagnostics/scoring/_scoring.py +1 -1
- model_diagnostics-1.5.0/src/model_diagnostics/xai/__init__.py +9 -0
- 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
- model_diagnostics-1.5.0/src/model_diagnostics/xai/_permutation_importance.py +291 -0
- model_diagnostics-1.5.0/src/model_diagnostics/xai/_plots.py +168 -0
- model_diagnostics-1.5.0/src/model_diagnostics/xai/tests/__init__.py +0 -0
- {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
- model_diagnostics-1.5.0/src/model_diagnostics/xai/tests/test_permutation_importance.py +216 -0
- model_diagnostics-1.5.0/src/model_diagnostics/xai/tests/test_plots.py +71 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/.codecov.yml +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/LICENSE +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/Untitled.ipynb +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/dev_tools/print_package_versions.py +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/docs/development.md +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/docs/examples/classification.ipynb +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/docs/examples/quantile_regression.ipynb +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/docs/examples/regression_on_workers_compensation.ipynb +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/docs/javascripts/mathjax.js +0 -0
- /model_diagnostics-1.4.3/mkdocs.yml → /model_diagnostics-1.5.0/properdocs.yml +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/__init__.py +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_config.py +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/__init__.py +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/isotonic.py +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/plot_helper.py +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/test_helper.py +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/_utils/tests/test_isotonic.py +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/calibration/tests/__init__.py +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/calibration/tests/test_plots.py +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/scoring/tests/__init__.py +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/scoring/tests/test_plots.py +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/scoring/tests/test_scoring.py +0 -0
- {model_diagnostics-1.4.3 → model_diagnostics-1.5.0}/src/model_diagnostics/tests/test_config.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: model-diagnostics
|
|
3
|
-
Version: 1.
|
|
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 |[](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml) [](https://codecov.io/gh/lorentzenchr/model-diagnostics)
|
|
51
|
+
| CI/CD |[](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml) [](https://codecov.io/gh/lorentzenchr/model-diagnostics) [](https://scorecard.dev/viewer/?uri=github.com/lorentzenchr/model-diagnostics)
|
|
52
52
|
| Docs | [](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/docs.yml)
|
|
53
53
|
| Package | [](https://pypi.org/project/model-diagnostics/) [](https://pypi.org/project/model-diagnostics/) [](https://pypi.org/project/model-diagnostics/) |
|
|
54
54
|
| Meta | [](https://github.com/pypa/hatch) [](https://github.com/astral-sh/ruff) [](https://github.com/python/mypy) [](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
|
|
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
|
|
64
|
-
- [bias plots](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration
|
|
65
|
-
- [marginal plots](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration
|
|
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/
|
|
68
|
-
- [score decomposition](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/scoring
|
|
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 |[](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml) [](https://codecov.io/gh/lorentzenchr/model-diagnostics)
|
|
5
|
+
| CI/CD |[](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml) [](https://codecov.io/gh/lorentzenchr/model-diagnostics) [](https://scorecard.dev/viewer/?uri=github.com/lorentzenchr/model-diagnostics)
|
|
6
6
|
| Docs | [](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/docs.yml)
|
|
7
7
|
| Package | [](https://pypi.org/project/model-diagnostics/) [](https://pypi.org/project/model-diagnostics/) [](https://pypi.org/project/model-diagnostics/) |
|
|
8
8
|
| Meta | [](https://github.com/pypa/hatch) [](https://github.com/astral-sh/ruff) [](https://github.com/python/mypy) [](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
|
|
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
|
|
18
|
-
- [bias plots](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration
|
|
19
|
-
- [marginal plots](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/calibration
|
|
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/
|
|
22
|
-
- [score decomposition](https://lorentzenchr.github.io/model-diagnostics/reference/model_diagnostics/scoring
|
|
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
|
-
|
|
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 |[](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml) [](https://codecov.io/gh/lorentzenchr/model-diagnostics)
|
|
5
|
+
| CI/CD |[](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/test.yml) [](https://codecov.io/gh/lorentzenchr/model-diagnostics) [](https://scorecard.dev/viewer/?uri=github.com/lorentzenchr/model-diagnostics)
|
|
6
6
|
| Docs | [](https://github.com/lorentzenchr/model-diagnostics/actions/workflows/docs.yml)
|
|
7
7
|
| Package | [](https://pypi.org/project/model-diagnostics/) [](https://pypi.org/project/model-diagnostics/) [](https://pypi.org/project/model-diagnostics/) |
|
|
8
8
|
| Meta | [](https://github.com/pypa/hatch) [](https://github.com/astral-sh/ruff) [](https://github.com/python/mypy) [](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.
|
|
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.
|
|
18
|
-
- [bias plots][model_diagnostics.calibration.
|
|
19
|
-
- [marginal plots][model_diagnostics.calibration.
|
|
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
|
|
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
|
-
"
|
|
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 = "
|
|
180
|
-
serve = "
|
|
181
|
-
ci-build = "
|
|
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
|
]
|
|
@@ -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)
|
|
267
|
-
values.dtype, pl.Categorical
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
|
277
|
-
if isinstance(values, pl.Series)
|
|
278
|
-
else values,
|
|
283
|
+
data=values,
|
|
279
284
|
dtype=dtype,
|
|
280
285
|
)
|
|
281
|
-
|
|
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
|
|
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
|
-
|
|
122
|
-
|
|
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)
|