Source code for improver.calibration.beta_recalibration

# (C) Crown Copyright, Met Office. All rights reserved.
#
# This file is part of 'IMPROVER' and is released under the BSD 3-Clause license.
# See LICENSE in the root of the repository for full licensing details.
"""Module containing class for recalibrating blended probabilities.

.. See the documentation for a more detailed discussion of this plugin.
.. include:: extended_documentation/calibration/beta_recalibration/
   beta_recalibration.rst
"""

from typing import Any, Dict

import cf_units
import iris
import numpy as np
from iris.exceptions import CoordinateNotFoundError
from scipy.stats import beta

from improver import PostProcessingPlugin
from improver.metadata.probabilistic import is_probability


[docs] class BetaRecalibrate(PostProcessingPlugin): """Recalibrate probabilities using the cumulative distribution function of the beta distribution. """
[docs] def __init__(self, recalibration_dict: Dict[str, Any]): """ Args: recalibration_dict: Dictionary from which to calculate alpha and beta parameters for recalibrating blended output using the beta distribution. Dictionary format is as specified below. Weights will be interpolated over the forecast period from the values specified in the dictionary. Recalibration dictionary format:: { "forecast_period": [7, 12], "alpha": [1, 1.5], "beta": [1.3, 2], "units": "hours", } The "units" key is optional. If it is omitted, it is assumed that the units are the same as those used in forecast_period coordinate of the input cube. """ self.recalibration_dict = recalibration_dict
[docs] def process(self, cube): """Recalibrate cube using the beta distribution with the alpha and beta parameters specified in self.recalibration_dict. Args: cube: A cube containing a forecast_period coordinate. Returns: A cube having the same dimensions as the input, with data transformed by the beta distribution cdf. Raises: CoordinateNotFoundError: if cube does not contain probability data CoordinateNotFoundError: if cube does not contain forecast_period coordinate RuntimeError: if any interpolated values of alpha or beta are <= 0 """ if not (is_probability(cube)): raise CoordinateNotFoundError( "Input cube must be a probability forecast " "and contain a threshold coordinate." ) if len(cube.coords("forecast_period")) == 0: raise CoordinateNotFoundError( "Recalibration input must contain forecast_period coordinate." ) forecast_period = self.recalibration_dict["forecast_period"] cube_forecast_period = cube.coord("forecast_period").points if "units" in self.recalibration_dict.keys(): # convert interpolation points to cube units units = cf_units.Unit(self.recalibration_dict["units"]) forecast_period = [ units.convert(x, cube.coord("forecast_period").units) for x in forecast_period ] a = np.interp( cube_forecast_period, forecast_period, self.recalibration_dict["alpha"] ) b = np.interp( cube_forecast_period, forecast_period, self.recalibration_dict["beta"] ) if np.any(a <= 0) or np.any(b <= 0): raise RuntimeError("interpolated alpha and beta parameters must be > 0") cubelist = iris.cube.CubeList([]) for i, slice in enumerate(cube.slices_over("forecast_period")): slice.data = beta(a[i], b[i]).cdf(slice.data).astype(np.float32) cubelist.append(slice) return cubelist.merge_cube()