コード例 #1
0
ファイル: test_utilities.py プロジェクト: zfan001/improver
 def test_no_matches_exception(self):
     """Test for when no matches in validity time are found between the
     historic forecasts and the truths. In this case, an exception is
     raised."""
     partial_truth = self.truth[2]
     msg = "The filtering has found no matches in validity time "
     with self.assertRaisesRegex(ValueError, msg):
         filter_non_matching_cubes(self.partial_historic_forecasts, partial_truth)
コード例 #2
0
 def test_fewer_truths(self):
     """Test for when there are fewer truths than historic forecasts,
     for example, if there is a missing analysis."""
     hf_result, truth_result = filter_non_matching_cubes(
         self.historic_temperature_forecast_cube, self.partial_truth)
     self.assertEqual(hf_result, self.partial_historic_forecasts)
     self.assertEqual(truth_result, self.partial_truth)
コード例 #3
0
 def test_fewer_historic_forecasts(self):
     """Test for when there are fewer historic forecasts than truths,
     for example, if there is a missing forecast cycle."""
     hf_result, truth_result = filter_non_matching_cubes(
         self.partial_historic_forecasts, self.temperature_truth_cube)
     self.assertEqual(hf_result, self.partial_historic_forecasts)
     self.assertEqual(truth_result, self.partial_truth)
コード例 #4
0
 def test_all_matching(self):
     """Test for when the historic forecast and truth cubes all match."""
     hf_result, truth_result = filter_non_matching_cubes(
         self.historic_temperature_forecast_cube,
         self.temperature_truth_cube)
     self.assertEqual(hf_result, self.historic_temperature_forecast_cube)
     self.assertEqual(truth_result, self.temperature_truth_cube)
コード例 #5
0
 def test_mismatching(self):
     """Test for when there is both a missing historic forecasts and a
     missing truth at different validity times. This results in the
     expected historic forecasts and the expected truths containing cubes
     at three matching validity times."""
     partial_truth = self.truth[1:].merge_cube()
     expected_historical_forecasts = iris.cube.CubeList([
         self.historic_forecasts[index] for index in (1, 3, 4)
     ]).merge_cube()
     expected_truth = iris.cube.CubeList(
         [self.truth[index] for index in (1, 3, 4)]).merge_cube()
     hf_result, truth_result = filter_non_matching_cubes(
         self.partial_historic_forecasts, partial_truth)
     self.assertEqual(hf_result, expected_historical_forecasts)
     self.assertEqual(truth_result, expected_truth)
コード例 #6
0
    def test_bounded_variables(self):
        """Test for when the historic forecast and truth cubes all match
        inclusive of both the points and bounds on the time coordinate."""
        # Define bounds so that the lower bound is one hour preceding the point
        # whilst the upper bound is equal to the point.
        points = self.historic_temperature_forecast_cube.coord("time").points
        bounds = []
        for point in points:
            bounds.append([point - 1 * 60 * 60, point])

        self.historic_temperature_forecast_cube.coord("time").bounds = bounds
        self.temperature_truth_cube.coord("time").bounds = bounds

        hf_result, truth_result = filter_non_matching_cubes(
            self.historic_temperature_forecast_cube,
            self.temperature_truth_cube)
        self.assertEqual(hf_result, self.historic_temperature_forecast_cube)
        self.assertEqual(truth_result, self.temperature_truth_cube)
コード例 #7
0
    def process(self, historic_forecasts, truths):
        """
        Slice data over threshold and time coordinates to construct reliability
        tables. These are summed over time to give a single table for each
        threshold, constructed from all the provided historic forecasts and
        truths.

        .. See the documentation for an example of the resulting reliability
           table cube.
        .. include:: extended_documentation/calibration/
           reliability_calibration/reliability_calibration_examples.rst

        Note that the forecast and truth data used is probabilistic, i.e. has
        already been thresholded relative to the thresholds of interest, using
        the equality operator required. As such this plugin is agnostic as to
        whether the data is thresholded below or above a given diagnostic
        threshold.

        Args:
            historic_forecasts (iris.cube.Cube):
                A cube containing the historical forecasts used in calibration.
                These are expected to all have a consistent cycle hour, that is
                the hour in the forecast reference time.
            truths (iris.cube.Cube):
                A cube containing the thresholded gridded truths used in
                calibration.
        Returns:
            iris.cube.CubeList:
                A cubelist of reliability table cubes, one for each threshold
                in the historic forecast cubes.
        Raises:
            ValueError: If the forecast and truth cubes have differing
                        threshold coordinates.
        """
        historic_forecasts, truths = filter_non_matching_cubes(
            historic_forecasts, truths)

        threshold_coord = find_threshold_coordinate(historic_forecasts)
        truth_threshold_coord = find_threshold_coordinate(truths)
        if not threshold_coord == truth_threshold_coord:
            msg = "Threshold coordinates differ between forecasts and truths."
            raise ValueError(msg)

        time_coord = historic_forecasts.coord("time")

        check_forecast_consistency(historic_forecasts)
        reliability_cube = self._create_reliability_table_cube(
            historic_forecasts, threshold_coord)

        reliability_tables = iris.cube.CubeList()
        threshold_slices = zip(
            historic_forecasts.slices_over(threshold_coord),
            truths.slices_over(threshold_coord),
        )
        for forecast_slice, truth_slice in threshold_slices:

            threshold_reliability = []
            time_slices = zip(
                forecast_slice.slices_over(time_coord),
                truth_slice.slices_over(time_coord),
            )
            for forecast, truth in time_slices:

                reliability_table = self._populate_reliability_bins(
                    forecast.data, truth.data)

                threshold_reliability.append(reliability_table)

            # Stack and sum reliability tables for all times
            table_values = np.stack(threshold_reliability)
            table_values = np.sum(table_values, axis=0, dtype=np.float32)

            reliability_entry = reliability_cube.copy(data=table_values)
            reliability_entry.replace_coord(
                forecast_slice.coord(threshold_coord))
            reliability_tables.append(reliability_entry)

        return MergeCubes()(reliability_tables)
コード例 #8
0
    def process(self, historic_forecast, truth, landsea_mask=None):
        """
        Using Nonhomogeneous Gaussian Regression/Ensemble Model Output
        Statistics, estimate the required coefficients from historical
        forecasts.

        The main contents of this method is:

        1. Check that the predictor is valid.
        2. Filter the historic forecasts and truth to ensure that these
           inputs match in validity time.
        3. Apply unit conversion to ensure that the historic forecasts and
           truth have the desired units for calibration.
        4. Calculate the variance of the historic forecasts. If the chosen
           predictor is the mean, also calculate the mean of the historic
           forecasts.
        5. If a land-sea mask is provided then mask out sea points in the truth
           and predictor from the historic forecasts.
        6. Calculate initial guess at coefficient values by performing a
           linear regression, if requested, otherwise default values are
           used.
        7. Perform minimisation.

        Args:
            historic_forecast (iris.cube.Cube):
                The cube containing the historical forecasts used
                for calibration.
            truth (iris.cube.Cube):
                The cube containing the truth used for calibration.
            landsea_mask (iris.cube.Cube):
                The optional cube containing a land-sea mask. If provided, only
                land points are used to calculate the coefficients. Within the
                land-sea mask cube land points should be specified as ones,
                and sea points as zeros.

        Returns:
            iris.cube.Cube:
                Cube containing the coefficients estimated using EMOS.
                The cube contains a coefficient_index dimension coordinate
                and a coefficient_name auxiliary coordinate.

        Raises:
            ValueError: If either the historic_forecast or truth cubes were not
                passed in.
            ValueError: If the units of the historic and truth cubes do not
                match.

        """
        if not (historic_forecast and truth):
            raise ValueError("historic_forecast and truth cubes must be "
                             "provided.")

        # Ensure predictor is valid.
        check_predictor(self.predictor)

        historic_forecast, truth = (
            filter_non_matching_cubes(historic_forecast, truth))

        # Make sure inputs have the same units.
        if self.desired_units:
            historic_forecast.convert_units(self.desired_units)
            truth.convert_units(self.desired_units)

        if historic_forecast.units != truth.units:
            msg = ("The historic forecast units of {} do not match "
                   "the truth units {}. These units must match, so that "
                   "the coefficients can be estimated.")
            raise ValueError(msg)

        if self.predictor.lower() == "mean":
            no_of_realizations = None
            forecast_predictor = collapsed(
                historic_forecast, "realization", iris.analysis.MEAN)
        elif self.predictor.lower() == "realizations":
            no_of_realizations = len(
                historic_forecast.coord("realization").points)
            forecast_predictor = historic_forecast

        forecast_var = collapsed(
            historic_forecast, "realization", iris.analysis.VARIANCE)

        # If a landsea_mask is provided mask out the sea points
        if landsea_mask:
            self.mask_cube(forecast_predictor, landsea_mask)
            self.mask_cube(forecast_var, landsea_mask)
            self.mask_cube(truth, landsea_mask)

        # Computing initial guess for EMOS coefficients
        initial_guess = self.compute_initial_guess(
            truth, forecast_predictor, self.predictor,
            self.ESTIMATE_COEFFICIENTS_FROM_LINEAR_MODEL_FLAG,
            no_of_realizations=no_of_realizations)

        # Calculate coefficients if there are no nans in the initial guess.
        if np.any(np.isnan(initial_guess)):
            optimised_coeffs = initial_guess
        else:
            optimised_coeffs = (
                self.minimiser(
                    initial_guess, forecast_predictor,
                    truth, forecast_var,
                    self.predictor,
                    self.distribution.lower()))
        coefficients_cube = (
            self.create_coefficients_cube(optimised_coeffs, historic_forecast))
        return coefficients_cube