scoringrules 0.4.2__tar.gz → 0.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.
- {scoringrules-0.4.2 → scoringrules-0.5.0}/PKG-INFO +1 -1
- {scoringrules-0.4.2 → scoringrules-0.5.0}/pyproject.toml +7 -6
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/__init__.py +2 -0
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/_brier.py +5 -5
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/_crps.py +52 -53
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/_energy.py +36 -40
- scoringrules-0.5.0/scoringrules/_error_spread.py +46 -0
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/_logs.py +6 -6
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/_variogram.py +29 -34
- scoringrules-0.5.0/scoringrules/backend/__init__.py +10 -0
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/backend/base.py +56 -0
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/backend/jax.py +42 -0
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/backend/numpy.py +63 -1
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/backend/registry.py +27 -31
- scoringrules-0.5.0/scoringrules/backend/tensorflow.py +253 -0
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/backend/torch.py +50 -3
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/core/brier.py +5 -5
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/core/crps/_approx.py +32 -32
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/core/crps/_closed.py +15 -9
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/core/crps/_gufuncs.py +63 -75
- scoringrules-0.5.0/scoringrules/core/energy/_gufuncs.py +91 -0
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/core/energy/_score.py +20 -24
- scoringrules-0.5.0/scoringrules/core/error_spread/__init__.py +4 -0
- scoringrules-0.5.0/scoringrules/core/error_spread/_gufunc.py +39 -0
- scoringrules-0.5.0/scoringrules/core/error_spread/_score.py +19 -0
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/core/logarithmic.py +3 -5
- scoringrules-0.5.0/scoringrules/core/stats.py +133 -0
- scoringrules-0.5.0/scoringrules/core/typing.py +13 -0
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/core/utils.py +11 -11
- scoringrules-0.5.0/scoringrules/core/variogram/_gufuncs.py +93 -0
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/core/variogram/_score.py +27 -27
- scoringrules-0.4.2/scoringrules/backend/__init__.py +0 -10
- scoringrules-0.4.2/scoringrules/core/energy/_gufuncs.py +0 -96
- scoringrules-0.4.2/scoringrules/core/stats.py +0 -24
- scoringrules-0.4.2/scoringrules/core/typing.py +0 -10
- scoringrules-0.4.2/scoringrules/core/variogram/_gufuncs.py +0 -96
- {scoringrules-0.4.2 → scoringrules-0.5.0}/LICENSE +0 -0
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/core/__init__.py +0 -0
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/core/crps/__init__.py +0 -0
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/core/energy/__init__.py +0 -0
- {scoringrules-0.4.2 → scoringrules-0.5.0}/scoringrules/core/variogram/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "scoringrules"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.5.0"
|
|
4
4
|
description = "Scoring rules for probabilistic forecast evaluation."
|
|
5
5
|
authors = [
|
|
6
6
|
"Francesco Zanetta <zanetta.francesco@gmail.com>",
|
|
@@ -19,16 +19,16 @@ numpy = "^1.23.4"
|
|
|
19
19
|
numba = "^0.57.0"
|
|
20
20
|
|
|
21
21
|
[tool.poetry.group.docs.dependencies]
|
|
22
|
-
mkdocs = "^1.
|
|
23
|
-
mkdocs-material = "^9.
|
|
24
|
-
mkdocstrings = { extras = ["python"], version = "^0.
|
|
22
|
+
mkdocs = "^1.5.3"
|
|
23
|
+
mkdocs-material = "^9.5.3"
|
|
24
|
+
mkdocstrings = { extras = ["python"], version = "^0.24.0" }
|
|
25
25
|
jupyter = "^1.0.0"
|
|
26
26
|
nbconvert = "7.3.1"
|
|
27
27
|
ipykernel = "6.22.0"
|
|
28
28
|
properscoring = "^0.1"
|
|
29
29
|
matplotlib = "^3.7.1"
|
|
30
|
-
mkdocs-bibtex = "^2.
|
|
31
|
-
mkdocs-section-index = "^0.3.
|
|
30
|
+
mkdocs-bibtex = "^2.11.0"
|
|
31
|
+
mkdocs-section-index = "^0.3.8"
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
[tool.poetry.group.dev.dependencies]
|
|
@@ -41,6 +41,7 @@ jax = { extras = ["cpu"], version = "^0.4.10" }
|
|
|
41
41
|
dask = "^2023.7.1"
|
|
42
42
|
xarray = "^2023.10.1"
|
|
43
43
|
torch = { version = ">=2.0.0, !=2.0.1, !=2.1.0" }
|
|
44
|
+
tensorflow = "^2.15.0"
|
|
44
45
|
|
|
45
46
|
[build-system]
|
|
46
47
|
requires = ["poetry-core>=1.0.0"]
|
|
@@ -16,6 +16,7 @@ from scoringrules._energy import (
|
|
|
16
16
|
twenergy_score,
|
|
17
17
|
vrenergy_score,
|
|
18
18
|
)
|
|
19
|
+
from scoringrules._error_spread import error_spread_score
|
|
19
20
|
from scoringrules._logs import logs_normal
|
|
20
21
|
from scoringrules._variogram import (
|
|
21
22
|
owvariogram_score,
|
|
@@ -40,6 +41,7 @@ __all__ = [
|
|
|
40
41
|
"vrcrps_ensemble",
|
|
41
42
|
"logs_normal",
|
|
42
43
|
"brier_score",
|
|
44
|
+
"error_spread_score",
|
|
43
45
|
"energy_score",
|
|
44
46
|
"owenergy_score",
|
|
45
47
|
"twenergy_score",
|
|
@@ -3,15 +3,15 @@ import typing as tp
|
|
|
3
3
|
from scoringrules.core import brier
|
|
4
4
|
|
|
5
5
|
if tp.TYPE_CHECKING:
|
|
6
|
-
from scoringrules.core.typing import Array, ArrayLike
|
|
6
|
+
from scoringrules.core.typing import Array, ArrayLike, Backend
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def brier_score(
|
|
10
|
-
forecasts: "ArrayLike",
|
|
11
10
|
observations: "ArrayLike",
|
|
11
|
+
forecasts: "ArrayLike",
|
|
12
12
|
/,
|
|
13
13
|
*,
|
|
14
|
-
backend:
|
|
14
|
+
backend: "Backend" = None,
|
|
15
15
|
) -> "Array":
|
|
16
16
|
r"""
|
|
17
17
|
Compute the Brier Score (BS).
|
|
@@ -24,10 +24,10 @@ def brier_score(
|
|
|
24
24
|
|
|
25
25
|
Parameters
|
|
26
26
|
----------
|
|
27
|
-
forecasts : NDArray
|
|
28
|
-
Forecasted probabilities between 0 and 1.
|
|
29
27
|
observations: NDArray
|
|
30
28
|
Observed outcome, either 0 or 1.
|
|
29
|
+
forecasts : NDArray
|
|
30
|
+
Forecasted probabilities between 0 and 1.
|
|
31
31
|
backend: str
|
|
32
32
|
The name of the backend used for computations. Defaults to 'numpy'.
|
|
33
33
|
|
|
@@ -4,28 +4,28 @@ from scoringrules.backend import backends
|
|
|
4
4
|
from scoringrules.core import crps
|
|
5
5
|
|
|
6
6
|
if tp.TYPE_CHECKING:
|
|
7
|
-
from scoringrules.core.typing import Array, ArrayLike
|
|
7
|
+
from scoringrules.core.typing import Array, ArrayLike, Backend
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def crps_ensemble(
|
|
11
|
-
forecasts: "Array",
|
|
12
11
|
observations: "ArrayLike",
|
|
12
|
+
forecasts: "Array",
|
|
13
13
|
/,
|
|
14
14
|
axis: int = -1,
|
|
15
15
|
*,
|
|
16
16
|
sorted_ensemble: bool = False,
|
|
17
17
|
estimator: str = "pwm",
|
|
18
|
-
backend:
|
|
18
|
+
backend: "Backend" = None,
|
|
19
19
|
) -> "Array":
|
|
20
20
|
r"""Estimate the Continuous Ranked Probability Score (CRPS) for a finite ensemble.
|
|
21
21
|
|
|
22
22
|
Parameters
|
|
23
23
|
----------
|
|
24
|
+
observations: ArrayLike
|
|
25
|
+
The observed values.
|
|
24
26
|
forecasts: ArrayLike
|
|
25
27
|
The predicted forecast ensemble, where the ensemble dimension is by default
|
|
26
28
|
represented by the last axis.
|
|
27
|
-
observations: ArrayLike
|
|
28
|
-
The observed values.
|
|
29
29
|
axis: int
|
|
30
30
|
The axis corresponding to the ensemble. Default is the last axis.
|
|
31
31
|
sorted_ensemble: bool
|
|
@@ -47,7 +47,7 @@ def crps_ensemble(
|
|
|
47
47
|
>>> crps.ensemble(pred, obs)
|
|
48
48
|
"""
|
|
49
49
|
B = backends.active if backend is None else backends[backend]
|
|
50
|
-
|
|
50
|
+
observations, forecasts = map(B.asarray, (observations, forecasts))
|
|
51
51
|
|
|
52
52
|
if estimator not in crps.estimator_gufuncs:
|
|
53
53
|
raise ValueError(
|
|
@@ -62,21 +62,21 @@ def crps_ensemble(
|
|
|
62
62
|
forecasts = B.sort(forecasts, axis=-1)
|
|
63
63
|
|
|
64
64
|
if backend == "numba":
|
|
65
|
-
return crps.estimator_gufuncs[estimator](
|
|
65
|
+
return crps.estimator_gufuncs[estimator](observations, forecasts)
|
|
66
66
|
|
|
67
|
-
return crps.ensemble(
|
|
67
|
+
return crps.ensemble(observations, forecasts, estimator, backend=backend)
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
def twcrps_ensemble(
|
|
71
|
-
forecasts: "Array",
|
|
72
71
|
observations: "ArrayLike",
|
|
72
|
+
forecasts: "Array",
|
|
73
73
|
v_func: tp.Callable[["ArrayLike"], "ArrayLike"],
|
|
74
74
|
/,
|
|
75
75
|
axis: int = -1,
|
|
76
76
|
*,
|
|
77
77
|
estimator: str = "pwm",
|
|
78
78
|
sorted_ensemble: bool = False,
|
|
79
|
-
backend:
|
|
79
|
+
backend: "Backend" = None,
|
|
80
80
|
) -> "Array":
|
|
81
81
|
r"""Estimate the Threshold-Weighted Continuous Ranked Probability Score (twCRPS) for a finite ensemble.
|
|
82
82
|
|
|
@@ -91,17 +91,15 @@ def twcrps_ensemble(
|
|
|
91
91
|
|
|
92
92
|
Parameters
|
|
93
93
|
----------
|
|
94
|
+
observations: ArrayLike
|
|
95
|
+
The observed values.
|
|
94
96
|
forecasts: ArrayLike
|
|
95
97
|
The predicted forecast ensemble, where the ensemble dimension is by default
|
|
96
98
|
represented by the last axis.
|
|
97
|
-
observations: ArrayLike
|
|
98
|
-
The observed values.
|
|
99
99
|
v_func: tp.Callable
|
|
100
100
|
Chaining function used to emphasise particular outcomes. For example, a function that
|
|
101
101
|
only considers values above a certain threshold $t$ by projecting forecasts and observations
|
|
102
|
-
to
|
|
103
|
-
v_funcargs: tuple
|
|
104
|
-
Additional arguments to the chaining function.
|
|
102
|
+
to $[t, \inf)$.
|
|
105
103
|
axis: int
|
|
106
104
|
The axis corresponding to the ensemble. Default is the last axis.
|
|
107
105
|
backend: str
|
|
@@ -117,10 +115,10 @@ def twcrps_ensemble(
|
|
|
117
115
|
>>> from scoringrules import crps
|
|
118
116
|
>>> twcrps.ensemble(pred, obs)
|
|
119
117
|
"""
|
|
120
|
-
|
|
118
|
+
observations, forecasts = map(v_func, (observations, forecasts))
|
|
121
119
|
return crps_ensemble(
|
|
122
|
-
forecasts,
|
|
123
120
|
observations,
|
|
121
|
+
forecasts,
|
|
124
122
|
axis=axis,
|
|
125
123
|
sorted_ensemble=sorted_ensemble,
|
|
126
124
|
estimator=estimator,
|
|
@@ -129,14 +127,14 @@ def twcrps_ensemble(
|
|
|
129
127
|
|
|
130
128
|
|
|
131
129
|
def owcrps_ensemble(
|
|
132
|
-
forecasts: "Array",
|
|
133
130
|
observations: "ArrayLike",
|
|
131
|
+
forecasts: "Array",
|
|
134
132
|
w_func: tp.Callable[["ArrayLike"], "ArrayLike"],
|
|
135
133
|
/,
|
|
136
134
|
axis: int = -1,
|
|
137
135
|
*,
|
|
138
136
|
estimator: tp.Literal["nrg"] = "nrg",
|
|
139
|
-
backend:
|
|
137
|
+
backend: "Backend" = None,
|
|
140
138
|
) -> "Array":
|
|
141
139
|
r"""Estimate the Outcome-Weighted Continuous Ranked Probability Score (owCRPS) for a finite ensemble.
|
|
142
140
|
|
|
@@ -151,15 +149,13 @@ def owcrps_ensemble(
|
|
|
151
149
|
|
|
152
150
|
Parameters
|
|
153
151
|
----------
|
|
152
|
+
observations: ArrayLike
|
|
153
|
+
The observed values.
|
|
154
154
|
forecasts: ArrayLike
|
|
155
155
|
The predicted forecast ensemble, where the ensemble dimension is by default
|
|
156
156
|
represented by the last axis.
|
|
157
|
-
observations: ArrayLike
|
|
158
|
-
The observed values.
|
|
159
157
|
w_func: tp.Callable
|
|
160
158
|
Weight function used to emphasise particular outcomes.
|
|
161
|
-
w_funcargs: tuple
|
|
162
|
-
Additional arguments to the weight function.
|
|
163
159
|
axis: int
|
|
164
160
|
The axis corresponding to the ensemble. Default is the last axis.
|
|
165
161
|
backend: str
|
|
@@ -185,27 +181,30 @@ def owcrps_ensemble(
|
|
|
185
181
|
if axis != -1:
|
|
186
182
|
forecasts = B.moveaxis(forecasts, axis, -1)
|
|
187
183
|
|
|
188
|
-
|
|
184
|
+
obs_weights, fct_weights = map(w_func, (observations, forecasts))
|
|
189
185
|
|
|
190
186
|
if backend == "numba":
|
|
191
187
|
return crps.estimator_gufuncs["ow" + estimator](
|
|
192
|
-
|
|
188
|
+
observations, forecasts, obs_weights, fct_weights
|
|
193
189
|
)
|
|
194
190
|
|
|
191
|
+
observations, forecasts, obs_weights, fct_weights = map(
|
|
192
|
+
B.asarray, (observations, forecasts, obs_weights, fct_weights)
|
|
193
|
+
)
|
|
195
194
|
return crps.ow_ensemble(
|
|
196
|
-
|
|
195
|
+
observations, forecasts, obs_weights, fct_weights, backend=backend
|
|
197
196
|
)
|
|
198
197
|
|
|
199
198
|
|
|
200
199
|
def vrcrps_ensemble(
|
|
201
|
-
forecasts: "Array",
|
|
202
200
|
observations: "ArrayLike",
|
|
201
|
+
forecasts: "Array",
|
|
203
202
|
w_func: tp.Callable[["ArrayLike"], "ArrayLike"],
|
|
204
203
|
/,
|
|
205
204
|
axis: int = -1,
|
|
206
205
|
*,
|
|
207
206
|
estimator: tp.Literal["nrg"] = "nrg",
|
|
208
|
-
backend:
|
|
207
|
+
backend: "Backend" = None,
|
|
209
208
|
) -> "Array":
|
|
210
209
|
r"""Estimate the Vertically Re-scaled Continuous Ranked Probability Score (vrCRPS) for a finite ensemble.
|
|
211
210
|
|
|
@@ -225,15 +224,13 @@ def vrcrps_ensemble(
|
|
|
225
224
|
|
|
226
225
|
Parameters
|
|
227
226
|
----------
|
|
227
|
+
observations: ArrayLike
|
|
228
|
+
The observed values.
|
|
228
229
|
forecasts: ArrayLike
|
|
229
230
|
The predicted forecast ensemble, where the ensemble dimension is by default
|
|
230
231
|
represented by the last axis.
|
|
231
|
-
observations: ArrayLike
|
|
232
|
-
The observed values.
|
|
233
232
|
w_func: tp.Callable
|
|
234
233
|
Weight function used to emphasise particular outcomes.
|
|
235
|
-
w_funcargs: tuple
|
|
236
|
-
Additional arguments to the weight function.
|
|
237
234
|
axis: int
|
|
238
235
|
The axis corresponding to the ensemble. Default is the last axis.
|
|
239
236
|
backend: str
|
|
@@ -259,25 +256,28 @@ def vrcrps_ensemble(
|
|
|
259
256
|
if axis != -1:
|
|
260
257
|
forecasts = B.moveaxis(forecasts, axis, -1)
|
|
261
258
|
|
|
262
|
-
|
|
259
|
+
obs_weights, fct_weights = map(w_func, (observations, forecasts))
|
|
263
260
|
|
|
264
261
|
if backend == "numba":
|
|
265
262
|
return crps.estimator_gufuncs["vr" + estimator](
|
|
266
|
-
|
|
263
|
+
observations, forecasts, obs_weights, fct_weights
|
|
267
264
|
)
|
|
268
265
|
|
|
266
|
+
observations, forecasts, obs_weights, fct_weights = map(
|
|
267
|
+
B.asarray, (observations, forecasts, obs_weights, fct_weights)
|
|
268
|
+
)
|
|
269
269
|
return crps.vr_ensemble(
|
|
270
|
-
|
|
270
|
+
observations, forecasts, obs_weights, fct_weights, backend=backend
|
|
271
271
|
)
|
|
272
272
|
|
|
273
273
|
|
|
274
274
|
def crps_normal(
|
|
275
|
+
observation: "ArrayLike",
|
|
275
276
|
mu: "ArrayLike",
|
|
276
277
|
sigma: "ArrayLike",
|
|
277
|
-
observation: "ArrayLike",
|
|
278
278
|
/,
|
|
279
279
|
*,
|
|
280
|
-
backend:
|
|
280
|
+
backend: "Backend" = None,
|
|
281
281
|
) -> "ArrayLike":
|
|
282
282
|
r"""Compute the closed form of the CRPS for the normal distribution.
|
|
283
283
|
|
|
@@ -291,12 +291,12 @@ def crps_normal(
|
|
|
291
291
|
|
|
292
292
|
Parameters
|
|
293
293
|
----------
|
|
294
|
+
observations: ArrayLike
|
|
295
|
+
The observed values.
|
|
294
296
|
mu: ArrayLike
|
|
295
297
|
Mean of the forecast normal distribution.
|
|
296
298
|
sigma: ArrayLike
|
|
297
299
|
Standard deviation of the forecast normal distribution.
|
|
298
|
-
observation: ArrayLike
|
|
299
|
-
The observed values.
|
|
300
300
|
|
|
301
301
|
Returns
|
|
302
302
|
-------
|
|
@@ -308,14 +308,14 @@ def crps_normal(
|
|
|
308
308
|
>>> from scoringrules import crps
|
|
309
309
|
>>> crps.normal(0.1, 0.4, 0.0)
|
|
310
310
|
"""
|
|
311
|
-
return crps.normal(mu, sigma,
|
|
311
|
+
return crps.normal(observation, mu, sigma, backend=backend)
|
|
312
312
|
|
|
313
313
|
|
|
314
314
|
def crps_lognormal(
|
|
315
|
+
observation: "ArrayLike",
|
|
315
316
|
mulog: "ArrayLike",
|
|
316
317
|
sigmalog: "ArrayLike",
|
|
317
|
-
|
|
318
|
-
backend: tp.Literal["numpy", "jax", "torch"] | None = None,
|
|
318
|
+
backend: "Backend" = None,
|
|
319
319
|
) -> "ArrayLike":
|
|
320
320
|
r"""Compute the closed form of the CRPS for the lognormal distribution.
|
|
321
321
|
|
|
@@ -329,9 +329,13 @@ def crps_lognormal(
|
|
|
329
329
|
where $\Phi$ is the CDF of the standard normal distribution and
|
|
330
330
|
$\omega = \frac{\mathrm{log}y - \mu}{\sigma}$.
|
|
331
331
|
|
|
332
|
+
Note that mean and standard deviation are not the values for the distribution itself,
|
|
333
|
+
but of the underlying normal distribution it is derived from.
|
|
332
334
|
|
|
333
335
|
Parameters
|
|
334
336
|
----------
|
|
337
|
+
observations: ArrayLike
|
|
338
|
+
The observed values.
|
|
335
339
|
mulog: ArrayLike
|
|
336
340
|
Mean of the normal underlying distribution.
|
|
337
341
|
sigmalog: ArrayLike
|
|
@@ -342,26 +346,21 @@ def crps_lognormal(
|
|
|
342
346
|
crps: ArrayLike
|
|
343
347
|
The CRPS between Lognormal(mu, sigma) and obs.
|
|
344
348
|
|
|
345
|
-
Notes
|
|
346
|
-
-----
|
|
347
|
-
The mean and standard deviation are not the values for the distribution itself,
|
|
348
|
-
but of the underlying normal distribution it is derived from.
|
|
349
|
-
|
|
350
349
|
Examples
|
|
351
350
|
--------
|
|
352
351
|
>>> from scoringrules import crps
|
|
353
352
|
>>> crps.lognormal(0.1, 0.4, 0.0)
|
|
354
353
|
"""
|
|
355
|
-
return crps.lognormal(mulog, sigmalog,
|
|
354
|
+
return crps.lognormal(observation, mulog, sigmalog, backend=backend)
|
|
356
355
|
|
|
357
356
|
|
|
358
357
|
def crps_logistic(
|
|
358
|
+
observation: "ArrayLike",
|
|
359
359
|
mu: "ArrayLike",
|
|
360
360
|
sigma: "ArrayLike",
|
|
361
|
-
observation: "ArrayLike",
|
|
362
361
|
/,
|
|
363
362
|
*,
|
|
364
|
-
backend:
|
|
363
|
+
backend: "Backend" = None,
|
|
365
364
|
) -> "ArrayLike":
|
|
366
365
|
r"""Compute the closed form of the CRPS for the logistic distribution.
|
|
367
366
|
|
|
@@ -375,12 +374,12 @@ def crps_logistic(
|
|
|
375
374
|
|
|
376
375
|
Parameters
|
|
377
376
|
----------
|
|
377
|
+
observations: ArrayLike
|
|
378
|
+
Observed values.
|
|
378
379
|
mu: ArrayLike
|
|
379
380
|
Location parameter of the forecast logistic distribution.
|
|
380
381
|
sigma: ArrayLike
|
|
381
382
|
Scale parameter of the forecast logistic distribution.
|
|
382
|
-
observation: ArrayLike
|
|
383
|
-
Observed values.
|
|
384
383
|
|
|
385
384
|
Returns
|
|
386
385
|
-------
|
|
@@ -392,7 +391,7 @@ def crps_logistic(
|
|
|
392
391
|
>>> from scoringrules import crps
|
|
393
392
|
>>> crps.logistic(0.1, 0.4, 0.0)
|
|
394
393
|
"""
|
|
395
|
-
return crps.logistic(mu, sigma,
|
|
394
|
+
return crps.logistic(observation, mu, sigma, backend=backend)
|
|
396
395
|
|
|
397
396
|
|
|
398
397
|
__all__ = [
|
|
@@ -5,24 +5,24 @@ from scoringrules.core import energy
|
|
|
5
5
|
from scoringrules.core.utils import multivariate_array_check
|
|
6
6
|
|
|
7
7
|
if tp.TYPE_CHECKING:
|
|
8
|
-
from scoringrules.core.typing import Array, ArrayLike
|
|
8
|
+
from scoringrules.core.typing import Array, ArrayLike, Backend
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def energy_score(
|
|
12
|
-
forecasts: "Array",
|
|
13
12
|
observations: "Array",
|
|
13
|
+
forecasts: "Array",
|
|
14
14
|
/,
|
|
15
15
|
m_axis: int = -2,
|
|
16
16
|
v_axis: int = -1,
|
|
17
17
|
*,
|
|
18
|
-
backend:
|
|
18
|
+
backend: "Backend" = None,
|
|
19
19
|
) -> "Array":
|
|
20
20
|
r"""Compute the Energy Score for a finite multivariate ensemble.
|
|
21
21
|
|
|
22
22
|
The Energy Score is a multivariate scoring rule expressed as
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
$$\text{ES}(F_{ens}, \mathbf{y})= \frac{1}{M} \sum_{m=1}^{M} \| \mathbf{x}_{m} -
|
|
25
|
+
\mathbf{y} \| - \frac{1}{2 M^{2}} \sum_{m=1}^{M} \sum_{j=1}^{M} \| \mathbf{x}_{m} - \mathbf{x}_{j} \| $$
|
|
26
26
|
|
|
27
27
|
where $\mathbf{X}$ and $\mathbf{X'}$ are independent samples from $F$
|
|
28
28
|
and $||\cdot||$ is the euclidean norm over the input dimensions (the variables).
|
|
@@ -30,11 +30,11 @@ def energy_score(
|
|
|
30
30
|
|
|
31
31
|
Parameters
|
|
32
32
|
----------
|
|
33
|
+
observations: Array
|
|
34
|
+
The observed values, where the variables dimension is by default the last axis.
|
|
33
35
|
forecasts: Array
|
|
34
36
|
The predicted forecast ensemble, where the ensemble dimension is by default
|
|
35
37
|
represented by the second last axis and the variables dimension by the last axis.
|
|
36
|
-
observations: Array
|
|
37
|
-
The observed values, where the variables dimension is by default the last axis.
|
|
38
38
|
m_axis: int
|
|
39
39
|
The axis corresponding to the ensemble dimension on the forecasts array. Defaults to -2.
|
|
40
40
|
v_axis: int
|
|
@@ -49,25 +49,25 @@ def energy_score(
|
|
|
49
49
|
The computed Energy Score.
|
|
50
50
|
"""
|
|
51
51
|
backend = backend if backend is not None else backends._active
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
observations, forecasts = multivariate_array_check(
|
|
53
|
+
observations, forecasts, m_axis, v_axis, backend=backend
|
|
54
54
|
)
|
|
55
55
|
|
|
56
56
|
if backend == "numba":
|
|
57
|
-
return energy._energy_score_gufunc(
|
|
57
|
+
return energy._energy_score_gufunc(observations, forecasts)
|
|
58
58
|
|
|
59
|
-
return energy.nrg(
|
|
59
|
+
return energy.nrg(observations, forecasts, backend=backend)
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
def twenergy_score(
|
|
63
|
-
forecasts: "Array",
|
|
64
63
|
observations: "Array",
|
|
64
|
+
forecasts: "Array",
|
|
65
65
|
v_func: tp.Callable[["ArrayLike"], "ArrayLike"],
|
|
66
66
|
/,
|
|
67
67
|
m_axis: int = -2,
|
|
68
68
|
v_axis: int = -1,
|
|
69
69
|
*,
|
|
70
|
-
backend:
|
|
70
|
+
backend: "Backend" = None,
|
|
71
71
|
) -> "Array":
|
|
72
72
|
r"""Compute the Threshold-Weighted Energy Score (twES) for a finite multivariate ensemble.
|
|
73
73
|
|
|
@@ -85,11 +85,11 @@ def twenergy_score(
|
|
|
85
85
|
|
|
86
86
|
Parameters
|
|
87
87
|
----------
|
|
88
|
+
observations: ArrayLike of shape (...,D)
|
|
89
|
+
The observed values, where the variables dimension is by default the last axis.
|
|
88
90
|
forecasts: ArrayLike of shape (..., M, D)
|
|
89
91
|
The predicted forecast ensemble, where the ensemble dimension is by default
|
|
90
92
|
represented by the second last axis and the variables dimension by the last axis.
|
|
91
|
-
observations: ArrayLike of shape (...,D)
|
|
92
|
-
The observed values, where the variables dimension is by default the last axis.
|
|
93
93
|
v_func: tp.Callable
|
|
94
94
|
Chaining function used to emphasise particular outcomes.
|
|
95
95
|
m_axis: int
|
|
@@ -104,21 +104,21 @@ def twenergy_score(
|
|
|
104
104
|
twenergy_score: ArrayLike of shape (...)
|
|
105
105
|
The computed Threshold-Weighted Energy Score.
|
|
106
106
|
"""
|
|
107
|
-
|
|
107
|
+
observations, forecasts = map(v_func, (observations, forecasts))
|
|
108
108
|
return energy_score(
|
|
109
|
-
|
|
109
|
+
observations, forecasts, m_axis=m_axis, v_axis=v_axis, backend=backend
|
|
110
110
|
)
|
|
111
111
|
|
|
112
112
|
|
|
113
113
|
def owenergy_score(
|
|
114
|
-
forecasts: "Array",
|
|
115
114
|
observations: "Array",
|
|
115
|
+
forecasts: "Array",
|
|
116
116
|
w_func: tp.Callable[["ArrayLike"], "ArrayLike"],
|
|
117
117
|
/,
|
|
118
118
|
m_axis: int = -2,
|
|
119
119
|
v_axis: int = -1,
|
|
120
120
|
*,
|
|
121
|
-
backend:
|
|
121
|
+
backend: "Backend" = None,
|
|
122
122
|
) -> "Array":
|
|
123
123
|
r"""Compute the Outcome-Weighted Energy Score (owES) for a finite multivariate ensemble.
|
|
124
124
|
|
|
@@ -136,15 +136,13 @@ def owenergy_score(
|
|
|
136
136
|
|
|
137
137
|
Parameters
|
|
138
138
|
----------
|
|
139
|
+
observations: ArrayLike of shape (...,D)
|
|
140
|
+
The observed values, where the variables dimension is by default the last axis.
|
|
139
141
|
forecasts: ArrayLike of shape (..., M, D)
|
|
140
142
|
The predicted forecast ensemble, where the ensemble dimension is by default
|
|
141
143
|
represented by the second last axis and the variables dimension by the last axis.
|
|
142
|
-
observations: ArrayLike of shape (...,D)
|
|
143
|
-
The observed values, where the variables dimension is by default the last axis.
|
|
144
144
|
w_func: tp.Callable
|
|
145
145
|
Weight function used to emphasise particular outcomes.
|
|
146
|
-
w_funcargs: tuple
|
|
147
|
-
Additional arguments to the weight function.
|
|
148
146
|
m_axis: int
|
|
149
147
|
The axis corresponding to the ensemble dimension. Defaults to -2.
|
|
150
148
|
v_axis: int or tuple(int)
|
|
@@ -159,32 +157,32 @@ def owenergy_score(
|
|
|
159
157
|
"""
|
|
160
158
|
B = backends.active if backend is None else backends[backend]
|
|
161
159
|
|
|
162
|
-
|
|
163
|
-
|
|
160
|
+
observations, forecasts = multivariate_array_check(
|
|
161
|
+
observations, forecasts, m_axis, v_axis, backend=backend
|
|
164
162
|
)
|
|
165
163
|
|
|
166
|
-
|
|
164
|
+
fct_weights = B.apply_along_axis(w_func, forecasts, -1)
|
|
167
165
|
obs_weights = B.apply_along_axis(w_func, observations, -1)
|
|
168
166
|
|
|
169
|
-
if
|
|
167
|
+
if B.name == "numba":
|
|
170
168
|
return energy._owenergy_score_gufunc(
|
|
171
|
-
|
|
169
|
+
observations, forecasts, obs_weights, fct_weights
|
|
172
170
|
)
|
|
173
171
|
|
|
174
172
|
return energy.ownrg(
|
|
175
|
-
|
|
173
|
+
observations, forecasts, obs_weights, fct_weights, backend=backend
|
|
176
174
|
)
|
|
177
175
|
|
|
178
176
|
|
|
179
177
|
def vrenergy_score(
|
|
180
|
-
forecasts: "Array",
|
|
181
178
|
observations: "Array",
|
|
179
|
+
forecasts: "Array",
|
|
182
180
|
w_func: tp.Callable[["ArrayLike"], "ArrayLike"],
|
|
183
181
|
/,
|
|
184
182
|
*,
|
|
185
183
|
m_axis: int = -2,
|
|
186
184
|
v_axis: int = -1,
|
|
187
|
-
backend:
|
|
185
|
+
backend: "Backend" = None,
|
|
188
186
|
) -> "Array":
|
|
189
187
|
r"""Compute the Vertically Re-scaled Energy Score (vrES) for a finite multivariate ensemble.
|
|
190
188
|
|
|
@@ -204,15 +202,13 @@ def vrenergy_score(
|
|
|
204
202
|
|
|
205
203
|
Parameters
|
|
206
204
|
----------
|
|
205
|
+
observations: ArrayLike of shape (...,D)
|
|
206
|
+
The observed values, where the variables dimension is by default the last axis.
|
|
207
207
|
forecasts: ArrayLike of shape (..., M, D)
|
|
208
208
|
The predicted forecast ensemble, where the ensemble dimension is by default
|
|
209
209
|
represented by the second last axis and the variables dimension by the last axis.
|
|
210
|
-
observations: ArrayLike of shape (...,D)
|
|
211
|
-
The observed values, where the variables dimension is by default the last axis.
|
|
212
210
|
w_func: tp.Callable
|
|
213
211
|
Weight function used to emphasise particular outcomes.
|
|
214
|
-
w_funcargs: tuple
|
|
215
|
-
Additional arguments to the weight function.
|
|
216
212
|
m_axis: int
|
|
217
213
|
The axis corresponding to the ensemble dimension. Defaults to -2.
|
|
218
214
|
v_axis: int or tuple(int)
|
|
@@ -227,18 +223,18 @@ def vrenergy_score(
|
|
|
227
223
|
"""
|
|
228
224
|
B = backends.active if backend is None else backends[backend]
|
|
229
225
|
|
|
230
|
-
|
|
231
|
-
|
|
226
|
+
observations, forecasts = multivariate_array_check(
|
|
227
|
+
observations, forecasts, m_axis, v_axis, backend=backend
|
|
232
228
|
)
|
|
233
229
|
|
|
234
|
-
|
|
230
|
+
fct_weights = B.apply_along_axis(w_func, forecasts, -1)
|
|
235
231
|
obs_weights = B.apply_along_axis(w_func, observations, -1)
|
|
236
232
|
|
|
237
233
|
if backend == "numba":
|
|
238
234
|
return energy._vrenergy_score_gufunc(
|
|
239
|
-
|
|
235
|
+
observations, forecasts, obs_weights, fct_weights
|
|
240
236
|
)
|
|
241
237
|
|
|
242
238
|
return energy.vrnrg(
|
|
243
|
-
|
|
239
|
+
observations, forecasts, obs_weights, fct_weights, backend=backend
|
|
244
240
|
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import typing as tp
|
|
2
|
+
|
|
3
|
+
from scoringrules.backend import backends
|
|
4
|
+
from scoringrules.core import error_spread
|
|
5
|
+
|
|
6
|
+
if tp.TYPE_CHECKING:
|
|
7
|
+
from scoringrules.core.typing import Array, ArrayLike, Backend
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def error_spread_score(
|
|
11
|
+
observations: "ArrayLike",
|
|
12
|
+
forecasts: "Array",
|
|
13
|
+
/,
|
|
14
|
+
axis: int = -1,
|
|
15
|
+
*,
|
|
16
|
+
backend: "Backend" = None,
|
|
17
|
+
) -> "Array":
|
|
18
|
+
r"""Compute the error-spread score [(Christensen et al., 2015)](https://doi.org/10.1002/qj.2375) for a finite ensemble.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
observations: ArrayLike
|
|
23
|
+
The observed values.
|
|
24
|
+
forecasts: Array
|
|
25
|
+
The predicted forecast ensemble, where the ensemble dimension is by default
|
|
26
|
+
represented by the last axis.
|
|
27
|
+
axis: int
|
|
28
|
+
The axis corresponding to the ensemble. Default is the last axis.
|
|
29
|
+
backend: str
|
|
30
|
+
The name of the backend used for computations. Defaults to 'numba' if available, else 'numpy'.
|
|
31
|
+
|
|
32
|
+
Returns
|
|
33
|
+
-------
|
|
34
|
+
- Array
|
|
35
|
+
An array of error spread scores for each ensemble forecast, which should be averaged to get meaningful values.
|
|
36
|
+
"""
|
|
37
|
+
B = backends.active if backend is None else backends[backend]
|
|
38
|
+
observations, forecasts = map(B.asarray, (observations, forecasts))
|
|
39
|
+
|
|
40
|
+
if axis != -1:
|
|
41
|
+
forecasts = B.moveaxis(forecasts, axis, -1)
|
|
42
|
+
|
|
43
|
+
if B.name == "numba":
|
|
44
|
+
return error_spread._ess_gufunc(observations, forecasts)
|
|
45
|
+
|
|
46
|
+
return error_spread.ess(observations, forecasts, backend=backend)
|