def process( forecast: cli.inputcube, reliability_table: cli.inputcubelist = None, ): """ Calibrate a probability forecast using the provided reliability calibration table. This calibration is designed to improve the reliability of probability forecasts without significantly degrading their resolution. If a reliability table is not provided, the input forecast is returned unchanged. The method implemented here is described in Flowerdew J. 2014. Calibrating ensemble reliability whilst preserving spatial structure. Tellus, Ser. A Dyn. Meteorol. Oceanogr. 66. Args: forecast (iris.cube.Cube): The forecast to be calibrated. reliability_table (iris.cube.Cube or iris.cube.CubeList): The reliability calibration table to use in calibrating the forecast. If input is a CubeList the CubeList should contain separate cubes for each threshold in the forecast cube. Returns: iris.cube.Cube: Calibrated forecast. """ from improver.calibration.reliability_calibration import ApplyReliabilityCalibration if reliability_table is None: return forecast plugin = ApplyReliabilityCalibration() return plugin(forecast, reliability_table)
def setUp(self): """Set up data for testing the interpolate method.""" self.reliability_probabilities = np.array([0.0, 0.4, 0.8]) self.observation_frequencies = np.array([0.2, 0.6, 1.0]) self.plugin = Plugin()
def test_values(self): """Test expected values are returned when two or more bins are available for interpolation.""" expected_0 = ( np.array([0.0, 0.25, 0.5, 0.75, 1.0]), np.array([0.0, 0.0, 0.25, 0.5, 0.75]), ) expected_1 = ( np.array([0.0, 0.25, 0.5, 0.75, 1.0]), np.array([0.25, 0.5, 0.75, 1.0, 1.0]), ) plugin = Plugin() threshold_0 = plugin._calculate_reliability_probabilities( self.reliability_cube[0]) threshold_1 = plugin._calculate_reliability_probabilities( self.reliability_cube[1]) assert_array_equal(threshold_0, expected_0) assert_array_equal(threshold_1, expected_1)
def process( forecast: cli.inputcube, reliability_table: cli.inputcube = None, *, minimum_forecast_count=200, ): """ Calibrate a probability forecast using the provided reliability calibration table. This calibration is designed to improve the reliability of probability forecasts without significantly degrading their resolution. If a reliability table is not provided, the input forecast is returned unchanged. The method implemented here is described in Flowerdew J. 2014. Calibrating ensemble reliability whilst preserving spatial structure. Tellus, Ser. A Dyn. Meteorol. Oceanogr. 66. Args: forecast (iris.cube.Cube): The forecast to be calibrated. reliability_table (iris.cube.Cube): The reliability calibration table to use in calibrating the forecast. minimum_forecast_count (int): The minimum number of forecast counts in a forecast probability bin for it to be used in calibration. If the reliability table for a forecast threshold includes any bins with insufficient counts that threshold will be returned unchanged. The default value of 200 is that used in Flowerdew 2014. Returns: iris.cube.Cube: Calibrated forecast. """ from improver.calibration.reliability_calibration import ApplyReliabilityCalibration if reliability_table is None: return forecast plugin = ApplyReliabilityCalibration( minimum_forecast_count=minimum_forecast_count) return plugin(forecast, reliability_table)
class Test__interpolate(unittest.TestCase): """Test the _interpolate method.""" def setUp(self): """Set up data for testing the interpolate method.""" self.reliability_probabilities = np.array([0.0, 0.4, 0.8]) self.observation_frequencies = np.array([0.2, 0.6, 1.0]) self.plugin = Plugin() def test_unmasked_data(self): """Test unmasked data is interpolated and returned as expected.""" expected = np.array([0.4, 0.6, 0.8]) forecast_threshold = np.array([0.2, 0.4, 0.6]) result = self.plugin._interpolate( forecast_threshold, self.reliability_probabilities, self.observation_frequencies, ) assert_allclose(result, expected) def test_masked_data(self): """Test masked data is interpolated and returned with the original mask in place.""" expected = np.ma.masked_array([np.nan, 0.6, 0.8], mask=[1, 0, 0]) forecast_threshold = np.ma.masked_array([np.nan, 0.4, 0.6], mask=[1, 0, 0]) result = self.plugin._interpolate( forecast_threshold, self.reliability_probabilities, self.observation_frequencies, ) assert_allclose(result, expected) def test_clipping(self): """Test the result, when using data constructed to cause extrapolation to a probability outside the range 0 to 1, is clipped. In this case an input probability of 0.9 would return a calibrated probability of 1.1 in the absence of clipping.""" expected = np.array([0.4, 0.6, 1.0]) forecast_threshold = np.array([0.2, 0.4, 0.9]) result = self.plugin._interpolate( forecast_threshold, self.reliability_probabilities, self.observation_frequencies, ) assert_allclose(result, expected) def test_reshaping(self): """Test that the result has the same shape as the forecast_threshold input data.""" expected = np.array([[0.2, 0.325, 0.45], [0.575, 0.7, 0.825], [0.95, 1.0, 1.0]]) forecast_threshold = np.linspace(0, 1, 9).reshape((3, 3)) result = self.plugin._interpolate( forecast_threshold, self.reliability_probabilities, self.observation_frequencies, ) self.assertEqual(result.shape, expected.shape) assert_allclose(result, expected)