Source code for dpyverification.scores.probabilistic
"""
Probabilistic verification scores.
For verification of probabilistic and ensemble forecasts, and probabilistic historical simulations
of continuous variables.
For reference, see: https://scores.readthedocs.io/en/stable/included.html#probability
"""
from typing import ClassVar
import xarray as xr
from scores.probability import crps_cdf, crps_for_ensemble # type: ignore[import-untyped]
from xskillscore import rank_histogram # type: ignore[import-untyped]
from dpyverification.configuration.default.scores import (
CrpsCDFConfig,
CrpsForEnsembleConfig,
RankHistogramConfig,
)
from dpyverification.constants import DataType, StandardDim
from dpyverification.scores.base import BaseScore
__all__ = [
"CrpsCDF",
"CrpsCDFConfig",
"CrpsForEnsemble",
"CrpsForEnsembleConfig",
"RankHistogram",
"RankHistogramConfig",
]
[docs]
class CrpsForEnsemble(BaseScore):
"""Implementation for CRPS for an ensemble."""
kind = "crps_for_ensemble"
config_class = CrpsForEnsembleConfig
supported_data_types: ClassVar[set[DataType]] = {
DataType.simulated_forecast_ensemble,
}
def __init__(self, config: CrpsForEnsembleConfig) -> None:
self.config: CrpsForEnsembleConfig = config
[docs]
def compute(
self,
obs: xr.DataArray,
sim: xr.DataArray,
) -> xr.Dataset | xr.DataArray:
"""Compute the CRPS for an ensemble of forecasts and observations."""
result: xr.DataArray | xr.Dataset = crps_for_ensemble(
fcst=sim,
obs=obs,
ensemble_member_dim=StandardDim.realization.value,
preserve_dims=self.config.preserve_dims,
)
return result
[docs]
class CrpsCDF(BaseScore):
"""Implementation for CRPS for probabilistic forecasts, expressed as cdf."""
kind = "crps_cdf"
config_class = CrpsCDFConfig
supported_data_types: ClassVar[set[DataType]] = {
DataType.simulated_forecast_probabilistic,
}
def __init__(self, config: CrpsCDFConfig) -> None:
self.config: CrpsCDFConfig = config
[docs]
def compute(self, obs: xr.DataArray, sim: xr.DataArray) -> xr.DataArray | xr.Dataset:
"""Compute the CRPS for an ensemble of forecasts and observations."""
result: xr.DataArray | xr.Dataset = crps_cdf(
fcst=sim,
obs=obs,
preserve_dims=self.config.preserve_dims,
)
# crps_cdf outputs a rather ambiguous variable 'total', hence rename to score kind.
return result.rename_vars({"total": str(self.config.score_adapter)}) # type:ignore[misc]
[docs]
class RankHistogram(BaseScore):
"""Compute the rank histogram (Talagrand diagram) over the specified dimensions.
For external documentation, see below:
https://xskillscore.readthedocs.io/en/stable/api/xskillscore.rank_histogram.html?highlight=rank%20histogram#xskillscore.rank_histogram
"""
kind = "rank_histogram"
config_class = RankHistogramConfig
supported_data_types: ClassVar[set[DataType]] = {
DataType.simulated_forecast_ensemble,
}
def __init__(self, config: RankHistogramConfig) -> None:
self.config: RankHistogramConfig = config
[docs]
def compute(self, obs: xr.DataArray, sim: xr.DataArray) -> xr.DataArray | xr.Dataset:
"""Compute the histogram of ranks over the specified dimensions."""
result: xr.DataArray | xr.Dataset = rank_histogram(
observations=obs,
forecasts=sim,
dim=self.config.preserve_dims,
member_dim=StandardDim.realization.value,
)
return result