def test_negative_percentiles(self):
     """
     Test that the plugin returns the expected values for the
     percentiles if negative probabilities are requested.
     """
     cube = self.temperature_cube
     percentiles = [-10, 10]
     plugin = Plugin()
     msg = "NaNs are present within the result for the"
     with self.assertRaisesRegex(ValueError, msg):
         plugin._location_and_scale_parameters_to_percentiles(
             self.location_parameter, self.scale_parameter, cube,
             percentiles)
    def test_if_nearly_identical_data(self):
        """
        Test that the plugin returns the expected values, if every
        percentile has an identical value. This causes an issue because
        the default for the underlying scipy function is to yield a NaN for
        tied values. For this application, any NaN values are overwritten with
        the predicted mean value for all probability thresholds.
        """
        data = np.array([
            [[[1.0, 1.0, 1.0], [4.0, 2.0, 2.0], [3.0, 3.0, 3.0]]],
            [[[1.0, 1.0, 1.0], [2.0, 2.0, 2.0], [3.0, 3.0, 3.0]]],
            [[[1.0, 1.0, 1.0], [2.0, 2.0, 2.0], [3.0, 3.0, 3.0]]],
        ])

        result_data = np.array([
            [[[1.0, 1.0, 1.0], [1.18685838, 2.0, 2.0], [3.0, 3.0, 3.0]]],
            [[[1.0, 1.0, 1.0], [2.66666667, 2.0, 2.0], [3.0, 3.0, 3.0]]],
            [[[1.0, 1.0, 1.0], [4.14647495, 2.0, 2.0], [3.0, 3.0, 3.0]]],
        ])

        cube = self.temperature_cube
        cube.data = data
        current_forecast_predictor = cube.collapsed("realization",
                                                    iris.analysis.MEAN)
        current_forecast_variance = cube.collapsed("realization",
                                                   iris.analysis.VARIANCE)
        plugin = Plugin()
        result = plugin._location_and_scale_parameters_to_percentiles(
            current_forecast_predictor,
            current_forecast_variance,
            cube,
            self.percentiles,
        )
        self.assertArrayAlmostEqual(result.data, result_data)
Exemple #3
0
    def test_if_identical_data(self):
        """
        Test that the plugin returns the expected values, if every
        percentile has an identical value. This causes an issue because
        the default for the underlying scipy function is to yield a NaN for
        tied values. For this application, any NaN values are overwritten with
        the predicted mean value for all probability thresholds.
        """
        data = np.array([[1, 1, 1], [2, 2, 2], [3, 3, 3]])
        # Repeat data in the realization dimension.
        data = np.repeat(data[np.newaxis, np.newaxis, :, :], 3, axis=0)

        result_data = np.array([[[[1., 1., 1.], [2., 2., 2.], [3., 3., 3.]]],
                                [[[1., 1., 1.], [2., 2., 2.], [3., 3., 3.]]],
                                [[[1., 1., 1.], [2., 2., 2.], [3., 3., 3.]]]])

        cube = self.temperature_cube
        cube.data = data
        current_forecast_predictor = cube.collapsed("realization",
                                                    iris.analysis.MEAN)
        current_forecast_variance = cube.collapsed("realization",
                                                   iris.analysis.VARIANCE)
        plugin = Plugin()
        result = plugin._location_and_scale_parameters_to_percentiles(
            current_forecast_predictor, current_forecast_variance, cube,
            self.percentiles)
        self.assertArrayAlmostEqual(result.data, result_data)
Exemple #4
0
    def test_simple_data(self):
        """
        Test that the plugin returns the expected values for the generated
        percentiles when an idealised set of data values between 1 and 3
        is used to create the mean (location parameter) and the variance
        (scale parameter).
        """
        data = np.array([[[[1, 1, 1], [1, 1, 1], [1, 1, 1]]],
                         [[[2, 2, 2], [2, 2, 2], [2, 2, 2]]],
                         [[[3, 3, 3], [3, 3, 3], [3, 3, 3]]]])

        result_data = np.array([[[[0.71844843, 0.71844843, 0.71844843],
                                  [0.71844843, 0.71844843, 0.71844843],
                                  [0.71844843, 0.71844843, 0.71844843]]],
                                [[[2., 2., 2.], [2., 2., 2.], [2., 2., 2.]]],
                                [[[3.28155157, 3.28155157, 3.28155157],
                                  [3.28155157, 3.28155157, 3.28155157],
                                  [3.28155157, 3.28155157, 3.28155157]]]])

        cube = self.temperature_cube
        cube.data = data
        current_forecast_predictor = cube.collapsed("realization",
                                                    iris.analysis.MEAN)
        current_forecast_variance = cube.collapsed("realization",
                                                   iris.analysis.VARIANCE)
        plugin = Plugin()
        result = plugin._location_and_scale_parameters_to_percentiles(
            current_forecast_predictor, current_forecast_variance, cube,
            self.percentiles)
        self.assertArrayAlmostEqual(result.data, result_data)
    def test_simple_data_truncnorm_distribution(self):
        """
        Test that the plugin returns an iris.cube.Cube matching the expected
        data values when cubes containing the location parameter and scale
        parameter are passed in. In this test, the ensemble mean and standard
        deviation is used as a proxy for the location and scale parameter.
        The resulting data values are the percentiles, which have been
        generated using a truncated normal distribution.
        """
        data = np.array([
            [[1, 1, 1], [1, 1, 1], [1, 1, 1]],
            [[2, 2, 2], [2, 2, 2], [2, 2, 2]],
            [[3, 3, 3], [3, 3, 3], [3, 3, 3]],
        ])
        self.temperature_cube.data = data

        expected_data = np.array([
            [
                [1.0121, 1.0121, 1.0121],
                [1.0121, 1.0121, 1.0121],
                [1.0121, 1.0121, 1.0121],
            ],
            [
                [3.1677, 3.1677, 3.1677],
                [3.1677, 3.1677, 3.1677],
                [3.1677, 3.1677, 3.1677],
            ],
            [
                [5.6412, 5.6412, 5.6412],
                [5.6412, 5.6412, 5.6412],
                [5.6412, 5.6412, 5.6412],
            ],
        ])

        # Use an adjusted version of the ensemble mean as a proxy for the
        # location parameter for the truncated normal distribution.
        current_forecast_predictor = self.temperature_cube.collapsed(
            "realization", iris.analysis.MEAN)
        current_forecast_predictor.data = current_forecast_predictor.data + 1
        # Use an adjusted version of the ensemble standard deviation as a proxy for the
        # scale parameter for the truncated normal distribution.
        current_forecast_stddev = self.temperature_cube.collapsed(
            "realization",
            iris.analysis.STD_DEV,
        )
        current_forecast_stddev.data = current_forecast_stddev.data + 1
        plugin = Plugin(
            distribution="truncnorm",
            shape_parameters=np.array([0, np.inf], dtype=np.float32),
        )
        result = plugin._location_and_scale_parameters_to_percentiles(
            current_forecast_predictor,
            current_forecast_stddev,
            self.temperature_cube,
            self.percentiles,
        )
        self.assertIsInstance(result, Cube)
        np.testing.assert_allclose(result.data, expected_data, rtol=1.0e-4)
    def test_simple_data_truncnorm_distribution(self):
        """
        Test that the plugin returns an iris.cube.Cube matching the expected
        data values when cubes containing the location parameter and scale
        parameter are passed in. In this test, the ensemble mean and variance
        is used as a proxy for the location and scale parameter. The resulting
        data values are the percentiles, which have been generated using a
        truncated normal distribution.
        """
        data = np.array([
            [[1, 1, 1], [1, 1, 1], [1, 1, 1]],
            [[2, 2, 2], [2, 2, 2], [2, 2, 2]],
            [[3, 3, 3], [3, 3, 3], [3, 3, 3]],
        ])
        self.temperature_cube.data = data

        expected_data = np.array([
            [
                [1.3042759, 1.3042759, 1.3042759],
                [1.3042759, 1.3042759, 1.3042759],
                [1.3042759, 1.3042759, 1.3042759],
            ],
            [
                [3.0300407, 3.0300407, 3.0300407],
                [3.0300407, 3.0300407, 3.0300407],
                [3.0300407, 3.0300407, 3.0300407],
            ],
            [
                [4.8261294, 4.8261294, 4.8261294],
                [4.8261294, 4.8261294, 4.8261294],
                [4.8261294, 4.8261294, 4.8261294],
            ],
        ])

        # Use an adjusted version of the ensemble mean as a proxy for the
        # location parameter for the truncated normal distribution.
        current_forecast_predictor = self.temperature_cube.collapsed(
            "realization", iris.analysis.MEAN)
        current_forecast_predictor.data = current_forecast_predictor.data + 1
        # Use an adjusted version of the ensemble variance as a proxy for the
        # scale parameter for the truncated normal distribution.
        current_forecast_variance = self.temperature_cube.collapsed(
            "realization", iris.analysis.VARIANCE)
        current_forecast_variance.data = current_forecast_variance.data + 1
        plugin = Plugin(
            distribution="truncnorm",
            shape_parameters=np.array([0, np.inf], dtype=np.float32),
        )
        result = plugin._location_and_scale_parameters_to_percentiles(
            current_forecast_predictor,
            current_forecast_variance,
            self.temperature_cube,
            self.percentiles,
        )
        self.assertIsInstance(result, Cube)
        self.assertArrayAlmostEqual(result.data, expected_data)
 def test_many_percentiles(self):
     """
     Test that the plugin returns an iris.cube.Cube if many percentiles
     are requested.
     """
     cube = self.temperature_cube
     percentiles = np.linspace(1, 99, num=1000, endpoint=True)
     plugin = Plugin()
     result = plugin._location_and_scale_parameters_to_percentiles(
         self.location_parameter, self.scale_parameter, cube, percentiles)
     self.assertIsInstance(result, Cube)
Exemple #8
0
 def test_check_data(self):
     """
     Test that the plugin returns an Iris.cube.Cube matching the expected
     data values when a cubes containing location and scale parameters are
     passed in, which are equivalent to the ensemble mean and ensemble
     variance. The resulting data values are the percentiles, which have
     been generated.
     """
     plugin = Plugin()
     result = plugin._location_and_scale_parameters_to_percentiles(
         self.location_parameter, self.scale_parameter,
         self.temperature_cube, self.percentiles)
     self.assertIsInstance(result, Cube)
     np.testing.assert_allclose(result.data, self.data, rtol=1.e-4)
Exemple #9
0
 def test_masked_scale_parameter(self):
     """
     Test that the plugin returns the correctly masked data when
     given a scale parameter that is masked.
     """
     mask = np.array([[0, 0, 0], [0, 0, 0], [1, 0, 1]])
     expected_mask = np.broadcast_to(mask, (3, 3, 3))
     expected_data = np.ma.masked_array(self.data, mask=expected_mask)
     self.scale_parameter.data = np.ma.masked_array(
         self.scale_parameter.data, mask=mask)
     plugin = Plugin()
     result = plugin._location_and_scale_parameters_to_percentiles(
         self.location_parameter, self.scale_parameter,
         self.temperature_cube, self.percentiles)
     np.testing.assert_allclose(result.data, expected_data, rtol=1.e-4)
Exemple #10
0
 def test_spot_forecasts_check_data(self):
     """
     Test that the plugin returns an Iris.cube.Cube matching the expected
     data values when a cube containing mean (location parameter) and
     variance (scale parameter) is passed in. The resulting data values are
     the percentiles, which have been generated for a spot forecast.
     """
     data = np.reshape(self.data, (3, 1, 9))
     cube = self.temperature_spot_cube
     current_forecast_predictor = cube.collapsed("realization",
                                                 iris.analysis.MEAN)
     current_forecast_variance = cube.collapsed("realization",
                                                iris.analysis.VARIANCE)
     plugin = Plugin()
     result = plugin._location_and_scale_parameters_to_percentiles(
         current_forecast_predictor, current_forecast_variance, cube,
         self.percentiles)
     self.assertIsInstance(result, Cube)
     self.assertArrayAlmostEqual(result.data, data)
 def test_both_masked(self):
     """
     Test that the plugin returns the correctly masked data when
     both the scale and location parameters are masked.
     """
     mask1 = np.array([[0, 1, 0], [0, 0, 0], [0, 0, 0]])
     mask2 = np.array([[0, 0, 0], [1, 0, 0], [0, 0, 0]])
     expected_mask = np.broadcast_to(mask1 + mask2, (3, 3, 3))
     expected_data = np.ma.masked_array(self.data, mask=expected_mask)
     self.location_parameter.data = np.ma.masked_array(
         self.location_parameter.data, mask=mask1)
     self.scale_parameter.data = np.ma.masked_array(
         self.scale_parameter.data, mask=mask2)
     plugin = Plugin()
     result = plugin._location_and_scale_parameters_to_percentiles(
         self.location_parameter,
         self.scale_parameter,
         self.temperature_cube,
         self.percentiles,
     )
     np.testing.assert_allclose(result.data, expected_data, rtol=1.0e-4)