예제 #1
0
 def test_unknown_sampling_option(self):
     """
     Test that the plugin returns the expected error message,
     if an unknown sampling option is selected.
     """
     no_of_percentiles = 3
     msg = "The unknown sampling option is not yet implemented"
     with self.assertRaisesRegex(ValueError, msg):
         choose_set_of_percentiles(no_of_percentiles, sampling="unknown")
예제 #2
0
    def _extract_error_percentiles(self, error_probability_cube,
                                   error_percentiles_count):
        """Extract error percentile values from the error exceedence probabilities.

        Args:
            error_probability_cube:
                A cube containing error exceedence probabilities.
            error_percentiles_count:
                The number of error percentiles to extract. The resulting percentiles
                will be evenly spaced over the interval (0, 100).

        Returns:
            Cube containing percentile values for the error distributions.
        """
        error_percentiles = choose_set_of_percentiles(
            error_percentiles_count,
            sampling="quantile",
        )
        error_percentiles_cube = ConvertProbabilitiesToPercentiles().process(
            error_probability_cube, percentiles=error_percentiles)
        if len(error_percentiles_cube.coord_dims("realization")) == 0:
            error_percentiles_cube = new_axis(error_percentiles_cube,
                                              "realization")

        return error_percentiles_cube
예제 #3
0
 def test_data(self):
     """
     Test that the plugin returns a list with the expected data values
     for the percentiles.
     """
     data = np.array([25, 50, 75])
     no_of_percentiles = 3
     result = choose_set_of_percentiles(no_of_percentiles)
     self.assertArrayAlmostEqual(result, data)
예제 #4
0
 def test_basic(self):
     """
     Test that the plugin returns a list with the expected number of
     percentiles.
     """
     no_of_percentiles = 3
     result = choose_set_of_percentiles(no_of_percentiles)
     self.assertIsInstance(result, list)
     self.assertEqual(len(result), no_of_percentiles)
예제 #5
0
 def test_random(self):
     """
     Test that the plugin returns a list with the expected number of
     percentiles, if the random sampling option is selected.
     """
     no_of_percentiles = 3
     result = choose_set_of_percentiles(no_of_percentiles, sampling="random")
     self.assertIsInstance(result, list)
     self.assertEqual(len(result), no_of_percentiles)
예제 #6
0
def _quantile_check(df: DataFrame) -> None:
    """Check that the percentiles provided can be considered to be
    quantiles with equal spacing spanning the percentile range.

    Args:
        df: DataFrame with a percentile column.

    Raises:
        ValueError: Percentiles are not equally spaced.
    """
    expected_percentiles = choose_set_of_percentiles(
        df["percentile"].nunique())

    if not np.allclose(expected_percentiles, df["percentile"].unique()):
        msg = ("The forecast percentiles can not be considered as quantiles. "
               f"The forecast percentiles are {df['percentile'].unique()}."
               "Based on the number of percentiles provided, the expected "
               f"percentiles would be {expected_percentiles}.")
        raise ValueError(msg)
예제 #7
0
    def _combine_subensembles(
            self, forecast_subensembles: Cube,
            output_realizations_count: Optional[int]) -> Cube:
        """Combine the forecast sub-ensembles into a single ensemble. This is done by
        first stacking the sub-ensembles into a single super-ensemble and then resampling
        the super-ensemble to produce a subset of output realizations.

        Args:
            forecast_subensembles:
                Cube containing a series of forecast sub-ensembles.
            output_realizations_count:
                The number of ensemble realizations that will be extracted from the
                super-ensemble. If realizations_count is None, all realizations will
                be returned.

        Returns:
            Cube containing single realization dimension.
        """
        superensemble_cube = self._stack_subensembles(forecast_subensembles)

        if output_realizations_count is None:
            warnings.warn(
                Warning(
                    "output_realizations_count not specified. Returning all realizations from the "
                    "full super-ensemble."))
            return superensemble_cube

        output_percentiles = choose_set_of_percentiles(
            output_realizations_count,
            sampling="quantile",
        )
        percentile_cube = generate_percentiles.process(
            superensemble_cube,
            coordinates="realization",
            percentiles=output_percentiles,
        )
        reduced_ensemble = RebadgePercentilesAsRealizations()(percentile_cube)

        return reduced_ensemble
예제 #8
0
def process(cube: cli.inputcube,
            *,
            coordinates: cli.comma_separated_list = None,
            ignore_ecc_bounds=False,
            percentiles: cli.comma_separated_list = None,
            percentiles_count: int = None):
    r"""Collapses cube coordinates and calculate percentiled data.

    Calculate percentiled data over a given coordinate by collapsing that
    coordinate. Typically used to convert realization data into percentiled
    data, but may calculate over any dimension coordinate. Alternatively
    calling this with a dataset containing probabilities will convert those
    to percentiles using the ensemble coupla coupling plugin. If no particular
    percentiles are given at which to calculate values and no
    'number of percentiles' to calculate are specified, the
    following defaults will be used.
    '[0, 5, 10, 20, 25, 30, 40, 50, 60, 70, 75, 80, 90, 95, 100]'

    Args:
        cube (iris.cube.Cube):
            A Cube for processing.
        coordinates (str or list):
            Coordinate or coordinates over which to collapse data and
            calculate percentiles; e.g. 'realization' or 'latitude,longitude'.
            This argument must be provided when collapsing a coordinate or
            coordinates to create percentiles, but is redundant when
            converting probabilities to percentiles and may be omitted. This
            coordinate(s) will be removed and replaced by a percentile
            coordinate.
        ignore_ecc_bounds (bool):
            If True, where calculated percentiles are outside the ECC bounds
            range, raises a warning rather than an exception.
        percentiles (list):
            Optional definition of percentiles at which to calculate data.
        percentiles_count (int):
            Optional definition of the number of percentiles to be generated,
            these distributed regularly with the aim of dividing into blocks
            of equal probability.

    Returns:
        iris.cube.Cube:
            The processed Cube.

    Raises:
        ValueError:
            If the cube name does not contain 'probability_of\_' and
            coordinates isn't used.

    Warns:
        Warning:
            If 'probability_of\_' is in the cube name and coordinates is used.

    """
    import warnings

    import numpy as np

    from improver.ensemble_copula_coupling.ensemble_copula_coupling import \
        GeneratePercentilesFromProbabilities
    from improver.ensemble_copula_coupling.utilities \
        import choose_set_of_percentiles
    from improver.percentile import PercentileConverter

    if percentiles is not None:
        percentiles = [float(p) for p in percentiles]

    if percentiles_count is not None:
        percentiles = choose_set_of_percentiles(percentiles_count,
                                                sampling="quantile")
    # TODO: Correct when formal cf-standards exists
    if 'probability_of_' in cube.name():
        result = GeneratePercentilesFromProbabilities(
            ecc_bounds_warning=ignore_ecc_bounds).process(
                cube, percentiles=percentiles)
        if coordinates:
            warnings.warn("Converting probabilities to percentiles. The "
                          "provided COORDINATES_TO_COLLAPSE variable will "
                          "not be used.")
    else:
        if not coordinates:
            raise ValueError("To collapse a coordinate to calculate "
                             "percentiles, a coordinate or list of "
                             "coordinates must be provided.")

        # Switch back to use the slow scipy method if the cube contains masked
        # data which the numpy method cannot handle.
        fast_percentile_method = True

        if np.ma.is_masked(cube.data):
            # Check for masked points:
            fast_percentile_method = False
        elif np.ma.isMaskedArray(cube.data):
            # Check if we have a masked array with an empty mask. If so,
            # replace it with a non-masked array:
            cube.data = cube.data.data

        result = PercentileConverter(
            coordinates,
            percentiles=percentiles,
            fast_percentile_method=fast_percentile_method).process(cube)
    return result