model-diagnostics 1.4.1__tar.gz → 1.4.3__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.1 → model_diagnostics-1.4.3}/PKG-INFO +9 -8
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/mkdocs.yml +2 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/pyproject.toml +31 -34
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/__about__.py +1 -1
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/_utils/binning.py +46 -37
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/_utils/partial_dependence.py +1 -1
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/_utils/tests/test_binning.py +1 -1
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/_utils/tests/test_isotonic.py +1 -1
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/_utils/tests/test_partial_dependence.py +6 -12
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/calibration/identification.py +26 -13
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/calibration/plots.py +22 -10
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/calibration/tests/test_identification.py +8 -2
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/calibration/tests/test_plots.py +11 -4
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/scoring/plots.py +1 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/scoring/scoring.py +29 -27
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/.codecov.yml +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/.gitignore +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/LICENSE +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/README.md +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/Untitled.ipynb +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/dev_tools/print_package_versions.py +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/docs/development.md +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/docs/examples/classification.ipynb +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/docs/examples/quantile_regression.ipynb +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/docs/examples/regression_on_workers_compensation.ipynb +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/docs/gen_ref_pages.py +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/docs/index.md +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/docs/javascripts/mathjax.js +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/__init__.py +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/_config.py +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/_utils/__init__.py +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/_utils/array.py +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/_utils/isotonic.py +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/_utils/plot_helper.py +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/_utils/test_helper.py +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/_utils/tests/test_array.py +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/calibration/__init__.py +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/calibration/tests/__init__.py +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/scoring/__init__.py +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/scoring/tests/__init__.py +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/scoring/tests/test_plots.py +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/scoring/tests/test_scoring.py +0 -0
- {model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/tests/test_config.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: model-diagnostics
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.3
|
|
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
|
|
@@ -27,6 +27,7 @@ License: MIT License
|
|
|
27
27
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
28
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
29
|
SOFTWARE.
|
|
30
|
+
License-File: LICENSE
|
|
30
31
|
Keywords: calibration,machine learning,model diagnostics
|
|
31
32
|
Classifier: Development Status :: 5 - Production/Stable
|
|
32
33
|
Classifier: Framework :: Hatch
|
|
@@ -34,13 +35,13 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
34
35
|
Classifier: Operating System :: OS Independent
|
|
35
36
|
Classifier: Programming Language :: Python :: 3
|
|
36
37
|
Classifier: Topic :: Scientific/Engineering
|
|
37
|
-
Requires-Python: >=3.
|
|
38
|
-
Requires-Dist: matplotlib>=3.
|
|
39
|
-
Requires-Dist: numpy>=
|
|
38
|
+
Requires-Python: >=3.11
|
|
39
|
+
Requires-Dist: matplotlib>=3.8.4
|
|
40
|
+
Requires-Dist: numpy>=2.0.0
|
|
40
41
|
Requires-Dist: packaging>=23.1
|
|
41
|
-
Requires-Dist: polars>=1.
|
|
42
|
-
Requires-Dist: scikit-learn>=1.2
|
|
43
|
-
Requires-Dist: scipy>=1.
|
|
42
|
+
Requires-Dist: polars>=1.1.0
|
|
43
|
+
Requires-Dist: scikit-learn>=1.4.2
|
|
44
|
+
Requires-Dist: scipy>=1.13.1
|
|
44
45
|
Description-Content-Type: text/markdown
|
|
45
46
|
|
|
46
47
|
# model-diagnostics
|
|
@@ -10,7 +10,7 @@ authors = [
|
|
|
10
10
|
description = "Tools for diagnostics and assessment of (machine learning) models"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
license = { file="LICENSE" }
|
|
13
|
-
requires-python = ">=3.
|
|
13
|
+
requires-python = ">=3.11"
|
|
14
14
|
classifiers = [
|
|
15
15
|
"Development Status :: 5 - Production/Stable",
|
|
16
16
|
"Framework :: Hatch",
|
|
@@ -21,12 +21,12 @@ classifiers = [
|
|
|
21
21
|
]
|
|
22
22
|
keywords = ["machine learning", "model diagnostics", "calibration"]
|
|
23
23
|
dependencies = [
|
|
24
|
-
"matplotlib>=3.
|
|
25
|
-
"numpy>=
|
|
24
|
+
"matplotlib>=3.8.4", # numpy 2 support
|
|
25
|
+
"numpy>=2.0.0",
|
|
26
26
|
"packaging>=23.1",
|
|
27
|
-
"polars>=1.
|
|
28
|
-
"scikit-learn>=1.2
|
|
29
|
-
"scipy>=1.
|
|
27
|
+
"polars>=1.1.0", # numpy 2 support
|
|
28
|
+
"scikit-learn>=1.4.2", # numpy 2 support
|
|
29
|
+
"scipy>=1.13.1", # numpy 2 support
|
|
30
30
|
]
|
|
31
31
|
dynamic = ["version"]
|
|
32
32
|
|
|
@@ -122,7 +122,6 @@ lint.isort.known-first-party = ["model_diagnostics"]
|
|
|
122
122
|
# disallow_untyped_defs = false
|
|
123
123
|
# follow_imports = "normal"
|
|
124
124
|
ignore_missing_imports = true
|
|
125
|
-
plugins = "numpy.typing.mypy_plugin"
|
|
126
125
|
pretty = true
|
|
127
126
|
# show_column_numbers = true
|
|
128
127
|
# warn_no_return = false
|
|
@@ -150,9 +149,10 @@ exclude = [
|
|
|
150
149
|
]
|
|
151
150
|
|
|
152
151
|
[tool.hatch.envs.default]
|
|
152
|
+
installer = "uv"
|
|
153
153
|
dependencies = [
|
|
154
|
-
"pandas>=
|
|
155
|
-
"pyarrow>=
|
|
154
|
+
"pandas>=2.2.2", # numpy 2 support
|
|
155
|
+
"pyarrow>=16.0.0", # numpy 2 support
|
|
156
156
|
"pytest",
|
|
157
157
|
"pytest-cov",
|
|
158
158
|
"pytest-xdist",
|
|
@@ -172,9 +172,7 @@ dependencies = [
|
|
|
172
172
|
"mkdocs-literate-nav>=0.6.1",
|
|
173
173
|
"mkdocs-section-index>=0.3.9",
|
|
174
174
|
"mkdocs-jupyter==0.24.7", # https://github.com/danielfrg/mkdocs-jupyter/issues/212
|
|
175
|
-
|
|
176
|
-
extra-dependencies = [
|
|
177
|
-
"scikit-learn>=1.3.0", # for Gamma deviance in HistGradientBoostingRegressor
|
|
175
|
+
"click<=8.2.1", # https://github.com/mkdocs/mkdocs/issues/4032
|
|
178
176
|
]
|
|
179
177
|
|
|
180
178
|
[tool.hatch.envs.docs.scripts]
|
|
@@ -185,7 +183,6 @@ ci-build = "mkdocs gh-deploy --force {args}"
|
|
|
185
183
|
[tool.hatch.envs.jupyter]
|
|
186
184
|
extra-dependencies = [
|
|
187
185
|
"jupyterlab>=4.0.4",
|
|
188
|
-
"scikit-learn>=1.3.0",
|
|
189
186
|
"plotly>=5.11.0",
|
|
190
187
|
]
|
|
191
188
|
|
|
@@ -195,7 +192,7 @@ lab = "jupyter lab"
|
|
|
195
192
|
[tool.hatch.envs.format]
|
|
196
193
|
detached = true
|
|
197
194
|
dependencies = [
|
|
198
|
-
"numpy>=
|
|
195
|
+
"numpy>=2.3",
|
|
199
196
|
"mypy>=1.14",
|
|
200
197
|
"ruff==0.4.4",
|
|
201
198
|
]
|
|
@@ -241,36 +238,36 @@ dependencies = [
|
|
|
241
238
|
]
|
|
242
239
|
|
|
243
240
|
[[tool.hatch.envs.test.matrix]]
|
|
244
|
-
python = ["3.
|
|
241
|
+
python = ["3.11", "3.12", "3.13", "3.14"]
|
|
245
242
|
|
|
246
243
|
[tool.hatch.envs.test.overrides]
|
|
247
|
-
# Minimal versions
|
|
244
|
+
# Minimal versions
|
|
248
245
|
# but including pandas and pyarrow
|
|
249
|
-
name."py3.
|
|
250
|
-
"numpy==
|
|
251
|
-
"polars==1.
|
|
252
|
-
"scipy==1.
|
|
253
|
-
"pandas==
|
|
254
|
-
"pyarrow==
|
|
246
|
+
name."py3.11".set-extra-dependencies = [
|
|
247
|
+
"numpy==2.0.0",
|
|
248
|
+
"polars==1.1.0",
|
|
249
|
+
"scipy==1.13.1",
|
|
250
|
+
"pandas==2.2.2", # numpy 2 support
|
|
251
|
+
"pyarrow==16.0.*" # numpy 2 support
|
|
255
252
|
]
|
|
256
253
|
# Minimal versions without pandas and pyarrow
|
|
257
|
-
name."py3.
|
|
258
|
-
"numpy==
|
|
259
|
-
"polars==1.
|
|
260
|
-
"scipy==1.
|
|
254
|
+
name."py3.12".set-extra-dependencies = [
|
|
255
|
+
"numpy==2.0.0",
|
|
256
|
+
"polars==1.1.0",
|
|
257
|
+
"scipy==1.13.1",
|
|
261
258
|
]
|
|
262
259
|
# Some intermediate versions, with pandads but without pyarrow
|
|
263
|
-
name."py3.
|
|
264
|
-
"numpy==1
|
|
265
|
-
"polars==1.
|
|
266
|
-
"scipy==1.
|
|
267
|
-
"pandas==2.
|
|
260
|
+
name."py3.13".set-extra-dependencies = [
|
|
261
|
+
"numpy==2.1.*",
|
|
262
|
+
"polars==1.10.*",
|
|
263
|
+
"scipy==1.14.*",
|
|
264
|
+
"pandas==2.3.*",
|
|
268
265
|
"plotly",
|
|
269
266
|
]
|
|
270
267
|
# Latest versions, with plotly
|
|
271
|
-
name."py3.
|
|
272
|
-
"numpy>=2.
|
|
273
|
-
"polars>=1.
|
|
268
|
+
name."py3.14".set-extra-dependencies = [
|
|
269
|
+
"numpy>=2.3.0",
|
|
270
|
+
"polars>=1.31.0",
|
|
274
271
|
"scipy",
|
|
275
272
|
"pandas",
|
|
276
273
|
"plotly",
|
|
@@ -30,8 +30,8 @@ def bin_feature(
|
|
|
30
30
|
feature_name: Optional[Union[int, str]],
|
|
31
31
|
n_obs: int,
|
|
32
32
|
n_bins: int = 10,
|
|
33
|
-
bin_method: str = "
|
|
34
|
-
):
|
|
33
|
+
bin_method: str = "sturges",
|
|
34
|
+
) -> tuple[pl.Series, int, pl.DataFrame]:
|
|
35
35
|
"""Helper function to bin features of different dtypes.
|
|
36
36
|
|
|
37
37
|
Best call this function inside a `with pl.StringCache()` context manager.
|
|
@@ -45,11 +45,15 @@ def bin_feature(
|
|
|
45
45
|
n_obs : int
|
|
46
46
|
The expected length of the first dimention of feature.
|
|
47
47
|
n_bins : int
|
|
48
|
-
The number of bins, at least 2
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
values
|
|
48
|
+
The number of bins, at least 2. For numerical features, `n_bins` only applies
|
|
49
|
+
when `bin_method` is set to `"quantile"` or `"uniform"`.
|
|
50
|
+
For string-like and categorical features, the most frequent values are taken.
|
|
51
|
+
Ties are dealt with by taking the first value in natural sorting order.
|
|
52
|
+
The remaining values are merged into `"other n"` with `n` indicating the unique
|
|
53
|
+
count.
|
|
54
|
+
|
|
55
|
+
I present, null values are always included in the output, accounting for one
|
|
56
|
+
bin. NaN values are treated as null values.
|
|
53
57
|
bin_method : str
|
|
54
58
|
The method to use for finding bin edges (boundaries). Options are:
|
|
55
59
|
|
|
@@ -115,7 +119,7 @@ def bin_feature(
|
|
|
115
119
|
# The following statement, i.e. possibly the creation of a pl.Categorical,
|
|
116
120
|
# MUST be under the StringCache context manager!
|
|
117
121
|
try:
|
|
118
|
-
|
|
122
|
+
feature_out = pl.Series(name=feature_name, values=feature)
|
|
119
123
|
except ImportError:
|
|
120
124
|
# FIXME: pyarrow not installed
|
|
121
125
|
# For non numpy-backed columns, pyarrow is needed. Here we handle the case
|
|
@@ -127,38 +131,38 @@ def bin_feature(
|
|
|
127
131
|
feature.dtype, # type: ignore
|
|
128
132
|
pandas.CategoricalDtype,
|
|
129
133
|
)
|
|
130
|
-
|
|
134
|
+
feature_out = pl.from_dataframe(
|
|
131
135
|
feature.to_frame(name=feature_name) # type: ignore
|
|
132
136
|
)[:, 0]
|
|
133
|
-
if is_pandas_categorical and isinstance(
|
|
137
|
+
if is_pandas_categorical and isinstance(feature_out.dtype, pl.Enum):
|
|
134
138
|
# Pandas categoricals usually get mapped to polars categoricals.
|
|
135
139
|
# But this code path gives pl.Enum.
|
|
136
|
-
|
|
140
|
+
feature_out = feature_out.cast(pl.Categorical)
|
|
137
141
|
else:
|
|
138
142
|
raise # re-raises the ImportError
|
|
139
|
-
if length_of_first_dimension(
|
|
143
|
+
if length_of_first_dimension(feature_out) != n_obs:
|
|
140
144
|
msg = (
|
|
141
145
|
f"The feature array {feature_name} does not have length {n_obs} of its"
|
|
142
146
|
" first dimension."
|
|
143
147
|
)
|
|
144
148
|
raise ValueError(msg)
|
|
145
|
-
if
|
|
149
|
+
if feature_out.dtype == pl.Categorical:
|
|
146
150
|
is_categorical = True
|
|
147
|
-
elif
|
|
151
|
+
elif feature_out.dtype == pl.Enum:
|
|
148
152
|
is_enum = True
|
|
149
|
-
elif
|
|
153
|
+
elif feature_out.dtype in [pl.Utf8, pl.Object]:
|
|
150
154
|
# We could convert strings to categoricals.
|
|
151
155
|
is_string = True
|
|
152
|
-
elif
|
|
156
|
+
elif feature_out.dtype.is_float():
|
|
153
157
|
# We treat NaN as Null values, numpy will see a Null as a NaN.
|
|
154
|
-
|
|
158
|
+
feature_out = feature_out.fill_nan(None)
|
|
155
159
|
else:
|
|
156
160
|
# integers
|
|
157
161
|
pass
|
|
158
162
|
|
|
159
163
|
# If we have Null values, we should reserve one bin for it and reduce
|
|
160
164
|
# the effective number of bins by 1.
|
|
161
|
-
n_bins_ef = max(1, n_bins -
|
|
165
|
+
n_bins_ef = max(1, n_bins - feature_out.has_nulls())
|
|
162
166
|
|
|
163
167
|
if is_categorical or is_enum or is_string:
|
|
164
168
|
# For categorical and string features, knowing the frequency table in
|
|
@@ -177,7 +181,7 @@ def bin_feature(
|
|
|
177
181
|
# value_counts(sort=True) sorts ties by first occurence, we want
|
|
178
182
|
# alphanumerical sorting order.
|
|
179
183
|
value_counts = (
|
|
180
|
-
|
|
184
|
+
feature_out.drop_nulls()
|
|
181
185
|
.value_counts()
|
|
182
186
|
.sort(by=["count", feature_name], descending=[True, False])
|
|
183
187
|
)
|
|
@@ -185,18 +189,21 @@ def bin_feature(
|
|
|
185
189
|
if n_bins_ef >= value_counts.shape[0]:
|
|
186
190
|
# This also covers the case of only null values.
|
|
187
191
|
n_bins_ef = value_counts.shape[0]
|
|
188
|
-
f_binned = pl.DataFrame({"bin":
|
|
192
|
+
f_binned = pl.DataFrame({"bin": feature_out})
|
|
189
193
|
else:
|
|
190
194
|
# We keep the n_bins_ef - 1 most frequent values. Ties are resolved by
|
|
191
195
|
# taking the first one of the sorted values (most often alpha-numerical).
|
|
192
|
-
if
|
|
196
|
+
if feature_out.has_nulls():
|
|
193
197
|
# To ease adding the null value, we take one value more.
|
|
194
198
|
keep_values = value_counts[feature_name].head(n_bins_ef)
|
|
195
|
-
if is_categorical:
|
|
196
|
-
# FIXME: Workaround for https://github.com/pola-rs/polars/issues/21175
|
|
199
|
+
if is_categorical or is_enum:
|
|
200
|
+
# FIXME: categorical: Workaround for https://github.com/pola-rs/polars/issues/21175
|
|
201
|
+
# FIXME: enum: Workaround for https://github.com/pola-rs/polars/pull/25245
|
|
202
|
+
# to be released in polars 1.35.3 or 1.36.
|
|
203
|
+
dtype = keep_values.dtype
|
|
197
204
|
keep_values = keep_values.cast(pl.String)
|
|
198
205
|
keep_values[-1] = None
|
|
199
|
-
keep_values = keep_values.cast(
|
|
206
|
+
keep_values = keep_values.cast(dtype)
|
|
200
207
|
else:
|
|
201
208
|
keep_values[-1] = None
|
|
202
209
|
else:
|
|
@@ -205,14 +212,16 @@ def bin_feature(
|
|
|
205
212
|
# n = n_remaining.
|
|
206
213
|
n_remaining = value_counts.shape[0] - (n_bins_ef - 1)
|
|
207
214
|
remaining_name = "other " + _format_integer(n_remaining)
|
|
208
|
-
while remaining_name in keep_values:
|
|
215
|
+
while remaining_name in keep_values.cast(pl.String):
|
|
209
216
|
remaining_name = "_" + remaining_name
|
|
210
|
-
return_dtype =
|
|
217
|
+
return_dtype = feature_out.dtype
|
|
211
218
|
if is_enum:
|
|
212
219
|
return_dtype = pl.Enum(
|
|
213
|
-
pl.concat(
|
|
220
|
+
pl.concat(
|
|
221
|
+
[feature_out.dtype.categories, pl.Series([remaining_name])]
|
|
222
|
+
)
|
|
214
223
|
)
|
|
215
|
-
f_binned =
|
|
224
|
+
f_binned = feature_out.replace_strict(
|
|
216
225
|
old=keep_values,
|
|
217
226
|
new=keep_values,
|
|
218
227
|
default=remaining_name,
|
|
@@ -222,13 +231,13 @@ def bin_feature(
|
|
|
222
231
|
else:
|
|
223
232
|
# Binning a numerical feature
|
|
224
233
|
# We will need min and max anyway.
|
|
225
|
-
feature_min, feature_max =
|
|
234
|
+
feature_min, feature_max = feature_out.min(), feature_out.max()
|
|
226
235
|
if feature_min == -np.inf:
|
|
227
|
-
finite_min =
|
|
236
|
+
finite_min = feature_out.filter(feature_out > -np.inf).min()
|
|
228
237
|
else:
|
|
229
238
|
finite_min = feature_min
|
|
230
239
|
if feature_max == np.inf:
|
|
231
|
-
finite_max =
|
|
240
|
+
finite_max = feature_out.filter(feature_out < np.inf).max()
|
|
232
241
|
else:
|
|
233
242
|
finite_max = feature_max
|
|
234
243
|
f_range = finite_max - finite_min
|
|
@@ -237,7 +246,7 @@ def bin_feature(
|
|
|
237
246
|
# We use method="inverted_cdf" instead of the default "linear" because
|
|
238
247
|
# "linear" produces as many unique values as before.
|
|
239
248
|
q = np.nanquantile(
|
|
240
|
-
|
|
249
|
+
feature_out,
|
|
241
250
|
# Improved rounding errors by using integers and dividing at the
|
|
242
251
|
# end as opposed to np.linspace with 1/n_bins step size.
|
|
243
252
|
q=np.arange(1, n_bins_ef) / n_bins_ef,
|
|
@@ -248,11 +257,11 @@ def bin_feature(
|
|
|
248
257
|
bin_edges = finite_min + f_range * np.arange(1, n_bins_ef) / n_bins_ef
|
|
249
258
|
else:
|
|
250
259
|
# numpy histogram bin methods
|
|
251
|
-
a =
|
|
260
|
+
a = feature_out.filter(feature_out.is_finite() & feature_out.is_not_null())
|
|
252
261
|
bin_edges = np.histogram_bin_edges(a, bins=bin_method)[1:-1]
|
|
253
262
|
n_bins_ef = bin_edges.shape[0] + 1
|
|
254
263
|
# We want: bins[i-1] < x <= bins[i]
|
|
255
|
-
f_binned = np.digitize(
|
|
264
|
+
f_binned = np.digitize(feature_out, bins=bin_edges, right=True)
|
|
256
265
|
# The full bin edges also include min and max of the feature.
|
|
257
266
|
if bin_edges.size == 0:
|
|
258
267
|
bin_edges = np.r_[feature_min, feature_max]
|
|
@@ -268,8 +277,8 @@ def bin_feature(
|
|
|
268
277
|
f_binned = (
|
|
269
278
|
pl.LazyFrame(
|
|
270
279
|
[
|
|
271
|
-
|
|
272
|
-
pl.Series("__f_binned", f_binned, dtype=
|
|
280
|
+
feature_out,
|
|
281
|
+
pl.Series("__f_binned", f_binned, dtype=feature_out.dtype),
|
|
273
282
|
pl.Series(
|
|
274
283
|
"__bin_edges",
|
|
275
284
|
bin_edges[f_binned],
|
|
@@ -289,4 +298,4 @@ def bin_feature(
|
|
|
289
298
|
)
|
|
290
299
|
.collect()
|
|
291
300
|
)
|
|
292
|
-
return
|
|
301
|
+
return feature_out, n_bins_ef + feature_out.has_nulls(), f_binned
|
|
@@ -146,7 +146,7 @@ def test_binning_numerical(bin_method, with_inf, with_null, feature_type):
|
|
|
146
146
|
|
|
147
147
|
|
|
148
148
|
def test_binning_auto():
|
|
149
|
-
"""
|
|
149
|
+
"""Test auto bin method"""
|
|
150
150
|
n_bins = 5
|
|
151
151
|
feature = pl.Series(name="my_feature", values=[0, 1, 1, 2])
|
|
152
152
|
n_obs = len(feature)
|
|
@@ -236,7 +236,7 @@ def test_gpava_simple(y, fun_lower, x_lower, fun_upper, x_upper):
|
|
|
236
236
|
tol=1e-12,
|
|
237
237
|
)
|
|
238
238
|
|
|
239
|
-
assert result.fun == pytest.approx(loss_lower, rel=1e-
|
|
239
|
+
assert result.fun == pytest.approx(loss_lower, rel=1e-7)
|
|
240
240
|
|
|
241
241
|
# Check that it is the boundary of solutions.
|
|
242
242
|
eps = 1e-8 * np.mean(np.abs(xl))
|
|
@@ -6,7 +6,7 @@ import polars as pl
|
|
|
6
6
|
import pytest
|
|
7
7
|
from numpy.testing import assert_allclose, assert_equal
|
|
8
8
|
from polars.testing import assert_frame_equal
|
|
9
|
-
from sklearn.base import RegressorMixin
|
|
9
|
+
from sklearn.base import BaseEstimator, RegressorMixin
|
|
10
10
|
from sklearn.inspection import partial_dependence
|
|
11
11
|
|
|
12
12
|
from model_diagnostics._utils.array import (
|
|
@@ -82,21 +82,15 @@ def test_compute_partial_dependence(n_max, weights, feature_type, data_container
|
|
|
82
82
|
"b": X["b"].to_numpy(),
|
|
83
83
|
}
|
|
84
84
|
)
|
|
85
|
-
elif feature_type == "string":
|
|
86
|
-
# TODO: With pandas 2.0.3, this does not work for strings. Get
|
|
87
|
-
# AssertionError from polars in string_column_to_ndarray.
|
|
88
|
-
# X_skl = pandas.api.interchange.from_dataframe(X). So we do it manually.
|
|
89
|
-
X_skl = pandas.DataFrame(
|
|
90
|
-
{
|
|
91
|
-
"a": X["a"].to_numpy(),
|
|
92
|
-
"b": X["b"].to_numpy(),
|
|
93
|
-
}
|
|
94
|
-
)
|
|
95
85
|
else:
|
|
96
86
|
X_skl = pandas.api.interchange.from_dataframe(X)
|
|
97
87
|
else:
|
|
98
88
|
X_skl = X.to_pandas()
|
|
99
89
|
|
|
90
|
+
if feature_type == "numeric" and pandas is not None:
|
|
91
|
+
# scikit-learn does not like integers in partial dependence
|
|
92
|
+
X_skl["a"] = 1.0 * X_skl["a"]
|
|
93
|
+
|
|
100
94
|
grid = X.get_column("a").unique().sort()
|
|
101
95
|
# Make sure grid is the same data container as X.
|
|
102
96
|
if data_container == "list":
|
|
@@ -143,7 +137,7 @@ def test_compute_partial_dependence(n_max, weights, feature_type, data_container
|
|
|
143
137
|
rng=rng,
|
|
144
138
|
)
|
|
145
139
|
|
|
146
|
-
class ModelWrapPredict(RegressorMixin):
|
|
140
|
+
class ModelWrapPredict(RegressorMixin, BaseEstimator):
|
|
147
141
|
def fit(self, X):
|
|
148
142
|
self.is_fitted_ = True
|
|
149
143
|
return self
|
|
@@ -60,6 +60,7 @@ def identification_function(
|
|
|
60
60
|
Notes
|
|
61
61
|
-----
|
|
62
62
|
[](){#notes}
|
|
63
|
+
|
|
63
64
|
The function \(V(y, z)\) for observation \(y=y_{pred}\) and prediction
|
|
64
65
|
\(z=y_{pred}\) is a strict identification function for the functional \(T\), or
|
|
65
66
|
induces the functional \(T\) as:
|
|
@@ -130,7 +131,7 @@ def compute_bias(
|
|
|
130
131
|
functional: str = "mean",
|
|
131
132
|
level: float = 0.5,
|
|
132
133
|
n_bins: int = 10,
|
|
133
|
-
bin_method: str = "
|
|
134
|
+
bin_method: str = "sturges",
|
|
134
135
|
):
|
|
135
136
|
r"""Compute generalised bias conditional on a feature.
|
|
136
137
|
|
|
@@ -172,10 +173,15 @@ def compute_bias(
|
|
|
172
173
|
`level=0.5` and `functional="expectile"` gives the mean.
|
|
173
174
|
`level=0.5` and `functional="quantile"` gives the median.
|
|
174
175
|
n_bins : int
|
|
175
|
-
The number of bins
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
176
|
+
The number of bins, at least 2. For numerical features, `n_bins` only applies
|
|
177
|
+
when `bin_method` is set to `"quantile"` or `"uniform"`.
|
|
178
|
+
For string-like and categorical features, the most frequent values are taken.
|
|
179
|
+
Ties are dealt with by taking the first value in natural sorting order.
|
|
180
|
+
The remaining values are merged into `"other n"` with `n` indicating the unique
|
|
181
|
+
count.
|
|
182
|
+
|
|
183
|
+
I present, null values are always included in the output, accounting for one
|
|
184
|
+
bin. NaN values are treated as null values.
|
|
179
185
|
bin_method : str
|
|
180
186
|
The method for finding bin edges (boundaries). Options using `n_bins` are:
|
|
181
187
|
|
|
@@ -230,6 +236,7 @@ def compute_bias(
|
|
|
230
236
|
Notes
|
|
231
237
|
-----
|
|
232
238
|
[](){#notes}
|
|
239
|
+
|
|
233
240
|
A model \(m(X)\) is conditionally calibrated iff
|
|
234
241
|
\(\mathbb{E}(V(m(X), Y)|X)=0\) almost surely with canonical identification
|
|
235
242
|
function \(V\).
|
|
@@ -306,20 +313,21 @@ def compute_bias(
|
|
|
306
313
|
with pl.StringCache():
|
|
307
314
|
feature_name = None
|
|
308
315
|
if feature is not None:
|
|
309
|
-
|
|
316
|
+
feature_series, n_bins, f_binned = bin_feature(
|
|
310
317
|
feature=feature,
|
|
311
318
|
feature_name=None,
|
|
312
319
|
n_obs=n_obs,
|
|
313
320
|
n_bins=n_bins,
|
|
314
321
|
bin_method=bin_method,
|
|
315
322
|
)
|
|
316
|
-
feature_name =
|
|
317
|
-
is_cat_or_string =
|
|
323
|
+
feature_name = feature_series.name
|
|
324
|
+
is_cat_or_string = feature_series.dtype in [
|
|
318
325
|
pl.Categorical,
|
|
319
326
|
pl.Enum,
|
|
320
327
|
pl.Utf8,
|
|
321
328
|
pl.Object,
|
|
322
329
|
]
|
|
330
|
+
feature = feature_series
|
|
323
331
|
|
|
324
332
|
for i in range(len(pred_names)):
|
|
325
333
|
# Loop over columns of y_pred.
|
|
@@ -483,7 +491,7 @@ def compute_marginal(
|
|
|
483
491
|
weights: Optional[npt.ArrayLike] = None,
|
|
484
492
|
*,
|
|
485
493
|
n_bins: int = 10,
|
|
486
|
-
bin_method: str = "
|
|
494
|
+
bin_method: str = "sturges",
|
|
487
495
|
n_max: int = 1000,
|
|
488
496
|
rng: Optional[Union[np.random.Generator, int]] = None,
|
|
489
497
|
):
|
|
@@ -512,10 +520,15 @@ def compute_marginal(
|
|
|
512
520
|
Case weights. If given, the bias is calculated as weighted average of the
|
|
513
521
|
identification function with these weights.
|
|
514
522
|
n_bins : int
|
|
515
|
-
The number of bins
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
523
|
+
The number of bins, at least 2. For numerical features, `n_bins` only applies
|
|
524
|
+
when `bin_method` is set to `"quantile"` or `"uniform"`.
|
|
525
|
+
For string-like and categorical features, the most frequent values are taken.
|
|
526
|
+
Ties are dealt with by taking the first value in natural sorting order.
|
|
527
|
+
The remaining values are merged into `"other n"` with `n` indicating the unique
|
|
528
|
+
count.
|
|
529
|
+
|
|
530
|
+
I present, null values are always included in the output, accounting for one
|
|
531
|
+
bin. NaN values are treated as null values.
|
|
519
532
|
bin_method : str
|
|
520
533
|
The method for finding bin edges (boundaries). Options using `n_bins` are:
|
|
521
534
|
|
{model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/calibration/plots.py
RENAMED
|
@@ -93,6 +93,7 @@ def plot_reliability_diagram(
|
|
|
93
93
|
Notes
|
|
94
94
|
-----
|
|
95
95
|
[](){#notes}
|
|
96
|
+
|
|
96
97
|
The expectation conditional on the predictions is \(E(Y|y_{pred})\). This object is
|
|
97
98
|
estimated by the pool-adjacent violator (PAV) algorithm, which has very desirable
|
|
98
99
|
properties:
|
|
@@ -319,7 +320,7 @@ def plot_bias(
|
|
|
319
320
|
functional: str = "mean",
|
|
320
321
|
level: float = 0.5,
|
|
321
322
|
n_bins: int = 10,
|
|
322
|
-
bin_method: str = "
|
|
323
|
+
bin_method: str = "sturges",
|
|
323
324
|
confidence_level: float = 0.9,
|
|
324
325
|
ax: Optional[mpl.axes.Axes] = None,
|
|
325
326
|
):
|
|
@@ -365,10 +366,15 @@ def plot_bias(
|
|
|
365
366
|
`level=0.5` and `functional="expectile"` gives the mean.
|
|
366
367
|
`level=0.5` and `functional="quantile"` gives the median.
|
|
367
368
|
n_bins : int
|
|
368
|
-
The number of bins
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
369
|
+
The number of bins, at least 2. For numerical features, `n_bins` only applies
|
|
370
|
+
when `bin_method` is set to `"quantile"` or `"uniform"`.
|
|
371
|
+
For string-like and categorical features, the most frequent values are taken.
|
|
372
|
+
Ties are dealt with by taking the first value in natural sorting order.
|
|
373
|
+
The remaining values are merged into `"other n"` with `n` indicating the unique
|
|
374
|
+
count.
|
|
375
|
+
|
|
376
|
+
I present, null values are always included in the output, accounting for one
|
|
377
|
+
bin. NaN values are treated as null values.
|
|
372
378
|
bin_method : str
|
|
373
379
|
The method for finding bin edges (boundaries). Options using `n_bins` are:
|
|
374
380
|
|
|
@@ -420,6 +426,7 @@ def plot_bias(
|
|
|
420
426
|
Notes
|
|
421
427
|
-----
|
|
422
428
|
[](){#notes}
|
|
429
|
+
|
|
423
430
|
A model \(m(X)\) is conditionally calibrated iff \(E(V(m(X), Y))=0\) a.s. The
|
|
424
431
|
empirical version, given some data, reads \(\frac{1}{n}\sum_i V(m(x_i), y_i)\).
|
|
425
432
|
See `[FLM2022]`.
|
|
@@ -742,7 +749,7 @@ def plot_marginal(
|
|
|
742
749
|
weights: Optional[npt.ArrayLike] = None,
|
|
743
750
|
*,
|
|
744
751
|
n_bins: int = 10,
|
|
745
|
-
bin_method: str = "
|
|
752
|
+
bin_method: str = "sturges",
|
|
746
753
|
n_max: int = 1000,
|
|
747
754
|
rng: Optional[Union[np.random.Generator, int]] = None,
|
|
748
755
|
ax: Optional[mpl.axes.Axes] = None,
|
|
@@ -773,10 +780,15 @@ def plot_marginal(
|
|
|
773
780
|
Case weights. If given, the bias is calculated as weighted average of the
|
|
774
781
|
identification function with these weights.
|
|
775
782
|
n_bins : int
|
|
776
|
-
The number of bins
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
783
|
+
The number of bins, at least 2. For numerical features, `n_bins` only applies
|
|
784
|
+
when `bin_method` is set to `"quantile"` or `"uniform"`.
|
|
785
|
+
For string-like and categorical features, the most frequent values are taken.
|
|
786
|
+
Ties are dealt with by taking the first value in natural sorting order.
|
|
787
|
+
The remaining values are merged into `"other n"` with `n` indicating the unique
|
|
788
|
+
count.
|
|
789
|
+
|
|
790
|
+
I present, null values are always included in the output, accounting for one
|
|
791
|
+
bin. NaN values are treated as null values.
|
|
780
792
|
bin_method : str
|
|
781
793
|
The method for finding bin edges (boundaries). Options using `n_bins` are:
|
|
782
794
|
|
|
@@ -296,7 +296,10 @@ def test_compute_bias_n_bins_string_like_feature(feature_type):
|
|
|
296
296
|
if feature_type == "cat":
|
|
297
297
|
dtype = pl.Categorical
|
|
298
298
|
elif feature_type == "cat_physical":
|
|
299
|
-
|
|
299
|
+
if Version(pl.__version__) < Version("1.32.0"):
|
|
300
|
+
dtype = pl.Categorical(ordering="lexical")
|
|
301
|
+
else:
|
|
302
|
+
dtype = pl.Categorical()
|
|
300
303
|
elif feature_type == "enum":
|
|
301
304
|
dtype = pl.Enum(categories=["b", "a", "c"])
|
|
302
305
|
else:
|
|
@@ -824,7 +827,10 @@ def test_compute_marginal_n_bins_string_like_feature(feature_type):
|
|
|
824
827
|
if feature_type == "cat":
|
|
825
828
|
dtype = pl.Categorical
|
|
826
829
|
elif feature_type == "cat_physical":
|
|
827
|
-
|
|
830
|
+
if Version(pl.__version__) < Version("1.32.0"):
|
|
831
|
+
dtype = pl.Categorical(ordering="lexical")
|
|
832
|
+
else:
|
|
833
|
+
dtype = pl.Categorical()
|
|
828
834
|
elif feature_type == "enum":
|
|
829
835
|
dtype = pl.Enum(categories=["b", "a", "c"])
|
|
830
836
|
else:
|
|
@@ -5,6 +5,7 @@ import matplotlib.pyplot as plt
|
|
|
5
5
|
import numpy as np
|
|
6
6
|
import polars as pl
|
|
7
7
|
import pytest
|
|
8
|
+
from packaging.version import Version
|
|
8
9
|
from sklearn.base import BaseEstimator, TransformerMixin
|
|
9
10
|
from sklearn.compose import ColumnTransformer
|
|
10
11
|
from sklearn.datasets import make_classification
|
|
@@ -305,7 +306,7 @@ def test_plot_bias(
|
|
|
305
306
|
random_state=42,
|
|
306
307
|
)
|
|
307
308
|
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
|
|
308
|
-
clf = LogisticRegression(solver="
|
|
309
|
+
clf = LogisticRegression(solver="newton-cg")
|
|
309
310
|
clf.fit(X_train, y_train)
|
|
310
311
|
feature = X_test[:, 0]
|
|
311
312
|
if feature_type != "num":
|
|
@@ -319,7 +320,10 @@ def test_plot_bias(
|
|
|
319
320
|
elif feature_type == "cat":
|
|
320
321
|
dtype = pl.Categorical
|
|
321
322
|
elif feature_type == "cat_physical":
|
|
322
|
-
|
|
323
|
+
if Version(pl.__version__) < Version("1.32.0"):
|
|
324
|
+
dtype = pl.Categorical(ordering="lexical")
|
|
325
|
+
else:
|
|
326
|
+
dtype = pl.Categorical()
|
|
323
327
|
elif feature_type == "enum":
|
|
324
328
|
dtype = pl.Enum(categories=np.unique(feature))
|
|
325
329
|
else:
|
|
@@ -527,7 +531,10 @@ def test_plot_marginal(with_null_values, feature_type, bin_method, ax, plot_back
|
|
|
527
531
|
elif feature_type == "cat":
|
|
528
532
|
dtype = pl.Categorical
|
|
529
533
|
elif feature_type == "cat_physical":
|
|
530
|
-
|
|
534
|
+
if Version(pl.__version__) < Version("1.32.0"):
|
|
535
|
+
dtype = pl.Categorical(ordering="lexical")
|
|
536
|
+
else:
|
|
537
|
+
dtype = pl.Categorical()
|
|
531
538
|
elif feature_type == "enum":
|
|
532
539
|
dtype = pl.Enum(categories=np.unique(feature))
|
|
533
540
|
else:
|
|
@@ -576,7 +583,7 @@ def test_plot_marginal(with_null_values, feature_type, bin_method, ax, plot_back
|
|
|
576
583
|
|
|
577
584
|
clf = make_pipeline(
|
|
578
585
|
ColumnTransformer([("ohe", safe_ohe, [0])], remainder="passthrough"),
|
|
579
|
-
LogisticRegression(solver="
|
|
586
|
+
LogisticRegression(solver="newton-cg"),
|
|
580
587
|
)
|
|
581
588
|
clf.fit(X, y)
|
|
582
589
|
|
{model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/scoring/scoring.py
RENAMED
|
@@ -60,7 +60,10 @@ class _BaseScoringFunction(ABC):
|
|
|
60
60
|
score : float
|
|
61
61
|
The average score.
|
|
62
62
|
"""
|
|
63
|
-
return np.average(
|
|
63
|
+
return np.average(
|
|
64
|
+
self.score_per_obs(y_obs, y_pred),
|
|
65
|
+
weights=weights, # type: ignore
|
|
66
|
+
)
|
|
64
67
|
|
|
65
68
|
@abstractmethod
|
|
66
69
|
def score_per_obs(
|
|
@@ -142,9 +145,9 @@ class HomogeneousExpectileScore(_BaseScoringFunction):
|
|
|
142
145
|
Examples
|
|
143
146
|
--------
|
|
144
147
|
>>> hes = HomogeneousExpectileScore(degree=2, level=0.1)
|
|
145
|
-
>>> hes(y_obs=[0, 0, 1, 1], y_pred=[-1, 1, 1 , 2])
|
|
146
|
-
0.95
|
|
147
|
-
"""
|
|
148
|
+
>>> hes(y_obs=[0, 0, 1, 1], y_pred=[-1, 1, 1 , 2])
|
|
149
|
+
np.float64(0.95)
|
|
150
|
+
"""
|
|
148
151
|
|
|
149
152
|
def __init__(self, degree: float = 2, level: float = 0.5) -> None:
|
|
150
153
|
self.degree = degree
|
|
@@ -268,9 +271,9 @@ class SquaredError(HomogeneousExpectileScore):
|
|
|
268
271
|
Examples
|
|
269
272
|
--------
|
|
270
273
|
>>> se = SquaredError()
|
|
271
|
-
>>> se(y_obs=[0, 0, 1, 1], y_pred=[-1, 1, 1 , 2])
|
|
272
|
-
0.75
|
|
273
|
-
"""
|
|
274
|
+
>>> se(y_obs=[0, 0, 1, 1], y_pred=[-1, 1, 1 , 2])
|
|
275
|
+
np.float64(0.75)
|
|
276
|
+
"""
|
|
274
277
|
|
|
275
278
|
def __init__(self) -> None:
|
|
276
279
|
super().__init__(degree=2, level=0.5)
|
|
@@ -296,9 +299,9 @@ class PoissonDeviance(HomogeneousExpectileScore):
|
|
|
296
299
|
Examples
|
|
297
300
|
--------
|
|
298
301
|
>>> pd = PoissonDeviance()
|
|
299
|
-
>>> pd(y_obs=[0, 0, 1, 1], y_pred=[2, 1, 1 , 2])
|
|
300
|
-
1.6534264097200273
|
|
301
|
-
"""
|
|
302
|
+
>>> pd(y_obs=[0, 0, 1, 1], y_pred=[2, 1, 1 , 2])
|
|
303
|
+
np.float64(1.6534264097200273)
|
|
304
|
+
"""
|
|
302
305
|
|
|
303
306
|
def __init__(self) -> None:
|
|
304
307
|
super().__init__(degree=1, level=0.5)
|
|
@@ -325,9 +328,9 @@ class GammaDeviance(HomogeneousExpectileScore):
|
|
|
325
328
|
Examples
|
|
326
329
|
--------
|
|
327
330
|
>>> gd = GammaDeviance()
|
|
328
|
-
>>> gd(y_obs=[3, 2, 1, 1], y_pred=[2, 1, 1 , 2])
|
|
329
|
-
0.2972674459459178
|
|
330
|
-
"""
|
|
331
|
+
>>> gd(y_obs=[3, 2, 1, 1], y_pred=[2, 1, 1 , 2])
|
|
332
|
+
np.float64(0.2972674459459178)
|
|
333
|
+
"""
|
|
331
334
|
|
|
332
335
|
def __init__(self) -> None:
|
|
333
336
|
super().__init__(degree=0, level=0.5)
|
|
@@ -366,11 +369,9 @@ class LogLoss(_BaseScoringFunction):
|
|
|
366
369
|
Examples
|
|
367
370
|
--------
|
|
368
371
|
>>> ll = LogLoss()
|
|
369
|
-
>>> ll(y_obs=[0, 0.5, 1, 1], y_pred=[0.1, 0.2, 0.8 , 0.9], weights=[1, 2, 1, 1])
|
|
370
|
-
0.17603033705165635
|
|
371
|
-
"""
|
|
372
|
-
|
|
373
|
-
# FIXME: numpy 2.0.0, doctest skip should not be necessary.
|
|
372
|
+
>>> ll(y_obs=[0, 0.5, 1, 1], y_pred=[0.1, 0.2, 0.8 , 0.9], weights=[1, 2, 1, 1])
|
|
373
|
+
np.float64(0.17603033705165635)
|
|
374
|
+
"""
|
|
374
375
|
|
|
375
376
|
@property
|
|
376
377
|
def functional(self):
|
|
@@ -466,9 +467,9 @@ class HomogeneousQuantileScore(_BaseScoringFunction):
|
|
|
466
467
|
Examples
|
|
467
468
|
--------
|
|
468
469
|
>>> hqs = HomogeneousQuantileScore(degree=3, level=0.1)
|
|
469
|
-
>>> hqs(y_obs=[0, 0, 1, 1], y_pred=[-1, 1, 1 , 2])
|
|
470
|
-
0.6083333333333334
|
|
471
|
-
"""
|
|
470
|
+
>>> hqs(y_obs=[0, 0, 1, 1], y_pred=[-1, 1, 1 , 2])
|
|
471
|
+
np.float64(0.6083333333333334)
|
|
472
|
+
"""
|
|
472
473
|
|
|
473
474
|
def __init__(self, degree: float = 2, level: float = 0.5) -> None:
|
|
474
475
|
self.degree = degree
|
|
@@ -569,9 +570,9 @@ class PinballLoss(HomogeneousQuantileScore):
|
|
|
569
570
|
Examples
|
|
570
571
|
--------
|
|
571
572
|
>>> pl = PinballLoss(level=0.9)
|
|
572
|
-
>>> pl(y_obs=[0, 0, 1, 1], y_pred=[-1, 1, 1 , 2])
|
|
573
|
-
0.275
|
|
574
|
-
"""
|
|
573
|
+
>>> pl(y_obs=[0, 0, 1, 1], y_pred=[-1, 1, 1 , 2])
|
|
574
|
+
np.float64(0.275)
|
|
575
|
+
"""
|
|
575
576
|
|
|
576
577
|
def __init__(self, level: float = 0.5) -> None:
|
|
577
578
|
super().__init__(degree=1, level=level)
|
|
@@ -606,6 +607,7 @@ class ElementaryScore(_BaseScoringFunction):
|
|
|
606
607
|
Notes
|
|
607
608
|
-----
|
|
608
609
|
[](){#notes}
|
|
610
|
+
|
|
609
611
|
The elementary scoring or loss function is given by
|
|
610
612
|
|
|
611
613
|
\[
|
|
@@ -644,9 +646,9 @@ class ElementaryScore(_BaseScoringFunction):
|
|
|
644
646
|
Examples
|
|
645
647
|
--------
|
|
646
648
|
>>> el_score = ElementaryScore(eta=2, functional="mean")
|
|
647
|
-
>>> el_score(y_obs=[1, 2, 2, 1], y_pred=[4, 1, 2, 3])
|
|
648
|
-
0.5
|
|
649
|
-
"""
|
|
649
|
+
>>> el_score(y_obs=[1, 2, 2, 1], y_pred=[4, 1, 2, 3])
|
|
650
|
+
np.float64(0.5)
|
|
651
|
+
"""
|
|
650
652
|
|
|
651
653
|
def __init__(
|
|
652
654
|
self, eta: float, functional: str = "mean", level: float = 0.5
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/_utils/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/_utils/isotonic.py
RENAMED
|
File without changes
|
{model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/_utils/plot_helper.py
RENAMED
|
File without changes
|
{model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/_utils/test_helper.py
RENAMED
|
File without changes
|
{model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/_utils/tests/test_array.py
RENAMED
|
File without changes
|
{model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/calibration/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/scoring/__init__.py
RENAMED
|
File without changes
|
{model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/scoring/tests/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{model_diagnostics-1.4.1 → model_diagnostics-1.4.3}/src/model_diagnostics/tests/test_config.py
RENAMED
|
File without changes
|