Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Parametric distributions forecasts
crps_exponentialM
crps_2pexponential
crps_gamma
crps_csg0
crps_gev
crps_gpd
crps_gtclogistic
Expand Down
2 changes: 2 additions & 0 deletions scoringrules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
crps_exponentialM,
crps_2pexponential,
crps_gamma,
crps_csg0,
crps_gev,
crps_gpd,
crps_gtclogistic,
Expand Down Expand Up @@ -139,6 +140,7 @@ def wrapper(*args, **kwargs):
"crps_exponentialM",
"crps_2pexponential",
"crps_gamma",
"crps_csg0",
"crps_gev",
"crps_gpd",
"crps_gtclogistic",
Expand Down
70 changes: 70 additions & 0 deletions scoringrules/_crps.py
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,75 @@ def crps_gamma(
return crps.gamma(obs, shape, rate, backend=backend)


def crps_csg0(
observation: "ArrayLike",
/,
shape: "ArrayLike",
rate: "ArrayLike | None" = None,
*,
scale: "ArrayLike | None" = None,
shift: "ArrayLike" = 0.0,
backend: "Backend" = None,
) -> "ArrayLike":
r"""Compute the closed form of the CRPS for the censored, shifted gamma distribution.

It is based on the following formulation from Appendix B of
[Scheuerer and Hamill (2015)](https://doi.org/10.1175/MWR-D-15-0061.1):

$$
\mathrm{CRPS}\bigl(F^0_{\alpha,\beta,\delta},\,y\bigr) =
(y+\delta)\,\bigl(2F_{\alpha,\beta}(y+\delta)-1\bigr)
- \frac{\alpha}{\beta\pi}\,B\!\Bigl(\tfrac12,\alpha+\tfrac12\Bigr)\,\bigl(1 - F_{2\alpha,\beta}(2\delta)\bigr)
+ \frac{\alpha}{\beta}\,\Bigl(1 + 2\,F_{\alpha,\beta}(\delta)\,F_{\alpha+1,\beta}(\delta) - F_{\alpha,\beta}(\delta)^2 - 2\,F_{\alpha+1,\beta}(y+\delta)\Bigr)
- \delta\,\bigl(F_{\alpha,\beta}(\delta)\bigr)^{2}
$$

where $F^0_{\alpha,\beta,\delta}$ is the censored, shifted gamma distribution function with
shape parameter $\alpha > 0$, rate parameter $\beta > 0$ (equivalently, with scale parameter $1/\beta$)
and shift parameter $\delta > 0$.

Parameters
----------
observation:
The observed values.
shape:
Shape parameter of the forecast CSG distribution.
rate:
Rate parameter of the forecast CSG distribution.
scale:
Scale parameter of the forecast CSG distribution, where `scale = 1 / rate`.
shift:
Shift parameter of the forecast CSG distribution.
backend:
The name of the backend used for computations. Defaults to 'numba' if available, else 'numpy'.

Returns
-------
score:
The CRPS between obs and CSG(shape, rate, shift).

Examples
--------
>>> import scoringrules as sr
>>> sr.crps_csg0(0.7, shape=0.5, rate=2.0, shift=0.3)
0.5411044348806484

Raises
------
ValueError
If both `rate` and `scale` are provided, or if neither is provided.
"""
if (scale is None and rate is None) or (scale is not None and rate is not None):
raise ValueError(
"Either `rate` or `scale` must be provided, but not both or neither."
)

if rate is None:
rate = 1.0 / scale

return crps.csg0(observation, shape, rate, shift, backend=backend)


def crps_gev(
obs: "ArrayLike",
shape: "ArrayLike",
Expand Down Expand Up @@ -2282,6 +2351,7 @@ def crps_uniform(
"crps_exponentialM",
"crps_2pexponential",
"crps_gamma",
"crps_csg0",
"crps_gev",
"crps_gpd",
"crps_gtclogistic",
Expand Down
2 changes: 2 additions & 0 deletions scoringrules/core/crps/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
exponentialM,
twopexponential,
gamma,
csg0,
gev,
gpd,
gtclogistic,
Expand Down Expand Up @@ -41,6 +42,7 @@
"exponentialM",
"twopexponential",
"gamma",
"csg0",
"gev",
"gpd",
"gtclogistic",
Expand Down
25 changes: 25 additions & 0 deletions scoringrules/core/crps/_closed.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,31 @@ def gamma(
return s


def csg0(
obs: "ArrayLike",
shape: "ArrayLike",
rate: "ArrayLike",
shift: "ArrayLike",
backend: "Backend" = None,
) -> "Array":
"""Compute the CRPS for the censored, shifted gamma distribution."""
B = backends.active if backend is None else backends[backend]
obs, shape, rate, shift = map(B.asarray, (obs, shape, rate, shift))
obs_shifted = obs + shift
F_ab_shifted = _gamma_cdf(obs_shifted, shape, rate, backend=backend)
F_2ab_2d = _gamma_cdf(2 * shift, 2 * shape, rate, backend=backend)
F_ab_d = _gamma_cdf(shift, shape, rate, backend=backend)
F_ab1_d = _gamma_cdf(shift, shape + 1, rate, backend=backend)
F_ab1_shifted = _gamma_cdf(obs_shifted, shape + 1, rate, backend=backend)
s = (
obs_shifted * (2 * F_ab_shifted - 1)
- (shape / (rate * B.pi)) * B.beta(B.asarray(0.5), shape + 0.5) * (1 - F_2ab_2d)
+ shape / rate * (1 + 2 * F_ab_d * F_ab1_d - F_ab_d**2 - 2 * F_ab1_shifted)
- shift * F_ab_d**2
)
return s


EULERMASCHERONI = 0.57721566490153286060651209008240243


Expand Down
26 changes: 26 additions & 0 deletions tests/test_crps.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,32 @@ def test_crps_gamma(backend):
return


@pytest.mark.parametrize("backend", BACKENDS)
def test_crps_csg0(backend):
obs, shape, rate, shift = 0.7, 0.5, 2.0, 0.3
expected = 0.5411044

expected_gamma = sr.crps_gamma(obs, shape, rate, backend=backend)
res_gamma = sr.crps_csg0(obs, shape=shape, rate=rate, shift=0.0, backend=backend)
assert np.isclose(res_gamma, expected_gamma)

res = sr.crps_csg0(obs, shape=shape, rate=rate, shift=shift, backend=backend)
assert np.isclose(res, expected)

res = sr.crps_csg0(obs, shape=shape, scale=1.0 / rate, shift=shift, backend=backend)
assert np.isclose(res, expected)

with pytest.raises(ValueError):
sr.crps_csg0(
obs, shape=shape, rate=rate, scale=1.0 / rate, shift=shift, backend=backend
)
return

with pytest.raises(ValueError):
sr.crps_csg0(obs, shape=shape, shift=shift, backend=backend)
return


@pytest.mark.parametrize("backend", BACKENDS)
def test_crps_gev(backend):
if backend == "torch":
Expand Down
Loading