Пример #1
0
    def projectField(self, cubes, neofs=None, eofscaling=0, weighted=True):
        """Project a set of fields onto the EOFs.

        Given a set of fields, projects them onto the EOFs to generate a
        corresponding set of pseudo-PCs.
        **Argument:**

        *fields*
            A list/tuple containing one or more `~iris.cube.Cube`
            instances, each with two or more dimensions, containing the
            data to be projected onto the EOFs. Each field must have the
            same spatial dimensions (including missing values in the
            same places) as the corresponding data set in the
            `MultivariateEof` input *datasets*. The fields may have
            different length time dimensions to the `MultivariateEof`
            inputs *datasets* or no time dimension at all, but this
            must be consistent for all fields.

        **Optional arguments:**

        *neofs*
            Number of EOFs to project onto. Defaults to all EOFs. If the
            number of EOFs requested is more than the number that are
            available, then the field will be projected onto all
            available EOFs.

        *eofscaling*
            Set the scaling of the EOFs that are projected
            onto. The following values are accepted:

            * *0* : Un-scaled EOFs (default).
            * *1* : EOFs are divided by the square-root of their
              eigenvalue.
            * *2* : EOFs are multiplied by the square-root of their
              eigenvalue.

        *weighted*
            If *True* then each field in *fields* is weighted using the
            same weights used for the EOF analysis prior to projection.
            If *False* then no weighting is applied. Defaults to *True*
            (weighting is applied). Generally only the default setting
            should be used.

        **Returns:**

        *pseudo_pcs*
            A `~iris.cube.Cube` containing the ordered pseudo-PCs.

        **Examples:**

        Project a data set onto all EOFs::

            pseudo_pcs = solver.projectField([field1, field2])

        Project a data set onto the four leading EOFs::

            pseudo_pcs = solver.projectField([field1, field2], neofs=4)

        """
        for cube in cubes:
            if type(cube) is not Cube:
                raise TypeError('input is not an iris cube')
        if len(cubes) != self._ncubes:
            raise ValueError('number of cubes is incorrect, expecting {:d} '
                             'but got {:d}'.format(self._ncubes, len(cubes)))
        for cube in cubes:
            try:
                # Time dimension must be first.
                raise_error = False
                time, time_coord = coord_and_dim(cube, 'time')
                if time_coord != 0:
                    raise_error = True
            except ValueError:
                # Not having a time dimension is also acceptable.
                pass
            if raise_error:
                raise ValueError('time must be the first dimension, '
                                 'consider using the transpose() method')
        # Compute the PCs.
        pcs = self._solver.projectField([cube.data for cube in cubes],
                                        neofs=neofs,
                                        eofscaling=eofscaling,
                                        weighted=weighted)
        # Construct the required dimensions.
        if pcs.ndim == 2:
            # 2D PCs require a time axis and a PC axis.
            pcdim = DimCoord(range(pcs.shape[1]),
                             var_name='pc',
                             long_name='pc_number')
            time, time_dim = coord_and_dim(cubes[0], 'time')
            coords = [time, pcdim]
        else:
            # 1D PCs require only a PC axis.
            pcdim = DimCoord(range(pcs.shape[0]),
                             var_name='pc',
                             long_name='pc_number')
            coords = [pcdim]
        # Construct an Iris cube.
        pcs = Cube(pcs,
                   dim_coords_and_dims=zip(coords, range(pcs.ndim)),
                   var_name='pseudo_pcs',
                   long_name='pseudo_pcs')
        return pcs
Пример #2
0
    def projectField(self, cubes, neofs=None, eofscaling=0, weighted=True):
        """Project a set of fields onto the EOFs.

        Given a set of fields, projects them onto the EOFs to generate a
        corresponding set of pseudo-PCs.
        **Argument:**

        *fields*
            A list/tuple containing one or more `~iris.cube.Cube`
            instances, each with two or more dimensions, containing the
            data to be projected onto the EOFs. Each field must have the
            same spatial dimensions (including missing values in the
            same places) as the corresponding data set in the
            `MultivariateEof` input *datasets*. The fields may have
            different length time dimensions to the `MultivariateEof`
            inputs *datasets* or no time dimension at all, but this
            must be consistent for all fields.

        **Optional arguments:**

        *neofs*
            Number of EOFs to project onto. Defaults to all EOFs. If the
            number of EOFs requested is more than the number that are
            available, then the field will be projected onto all
            available EOFs.

        *eofscaling*
            Set the scaling of the EOFs that are projected
            onto. The following values are accepted:

            * *0* : Un-scaled EOFs (default).
            * *1* : EOFs are divided by the square-root of their
              eigenvalue.
            * *2* : EOFs are multiplied by the square-root of their
              eigenvalue.

        *weighted*
            If *True* then each field in *fields* is weighted using the
            same weights used for the EOF analysis prior to projection.
            If *False* then no weighting is applied. Defaults to *True*
            (weighting is applied). Generally only the default setting
            should be used.

        **Returns:**

        *pseudo_pcs*
            A `~iris.cube.Cube` containing the ordered pseudo-PCs.

        **Examples:**

        Project a data set onto all EOFs::

            pseudo_pcs = solver.projectField([field1, field2])

        Project a data set onto the four leading EOFs::

            pseudo_pcs = solver.projectField([field1, field2], neofs=4)

        """
        for cube in cubes:
            if not isinstance(cube, Cube):
                raise TypeError('input is not an iris cube')
        if len(cubes) != self._ncubes:
            raise ValueError('number of cubes is incorrect, expecting {:d} '
                             'but got {:d}'.format(self._ncubes, len(cubes)))
        _all_time_aux_coords = []
        for cube in cubes:
            try:
                # Time dimension must be first.
                raise_error = False
                time, time_coord = coord_and_dim(cube, 'time')
                if time_coord != 0:
                    raise_error = True
            except ValueError:
                # Not having a time dimension is also acceptable.
                pass
            if raise_error:
                raise ValueError('time must be the first dimension, '
                                 'consider using the transpose() method')
            # Store any AuxCoords describing the time dimension.
            _t, _, _ = classified_aux_coords(cube)
            _all_time_aux_coords.append(_t)
        # Retain AuxCoords that describe the time dimension of *every* input
        # cube.
        _common_time_aux_coords = common_items(_all_time_aux_coords)
        # Compute the PCs.
        pcs = self._solver.projectField([cube.data for cube in cubes],
                                        neofs=neofs,
                                        eofscaling=eofscaling,
                                        weighted=weighted)
        pcs = Cube(pcs, var_name='pseudo_pcs', long_name='pseudo_pcs')
        # Construct the required dimensions.
        if pcs.ndim == 2:
            # 2D PCs require a time axis and a PC axis.
            pcdim = DimCoord(range(pcs.shape[1]),
                             var_name='pc',
                             long_name='pc_number')
            time, time_dim = coord_and_dim(cubes[0], 'time')
            pcs.add_dim_coord(copy(time), 0)
            pcs.add_dim_coord(pcdim, 1)
            # Add any auxiliary coordinates for the time dimension.
            for coord, dims in _common_time_aux_coords:
                pcs.add_aux_coord(copy(coord), dims)
        else:
            # 1D PCs require only a PC axis.
            pcdim = DimCoord(range(pcs.shape[0]),
                             var_name='pc',
                             long_name='pc_number')
            pcs.add_dim_coord(pcdim, 0)
        return pcs
Пример #3
0
    def __init__(self, cubes, weights=None, center=True, ddof=1):
        """Create a MultivariateEof instance.

        The EOF solution is computed at initialization time. Method
        calls are used to retrieve computed quantities.

        **Arguments:**

        *cubes*
            A list/tuple containing one or more `~iris.cube.Cube`
            instances, each with two or more dimensions, containing the
            data to be analysed. Time must be the first dimension of
            each `~iris.cube.Cube`. Missing values are allowed provided
            that they are constant with time in each field (e.g., values
            of an oceanographic field over land).

        **Optional arguments:**

        *weights*
            Sets the weighting method. One method can be chosen to apply
            to all cubes in *datasets* or a sequence of options can be
            given to specify a different weighting method for each cube
            in *datasets*. The following pre-defined weighting methods
            are available:

            * *'area'* : Square-root of grid cell area normalized by
              total grid area. Requires a latitude-longitude grid to be
              present in the corresponding `~iris.cube.Cube`. This is a
              fairly standard weighting strategy. If you are unsure
              which method to use and you have gridded data then this
              should be your first choice.

            * *'coslat'* : Square-root of cosine of latitude. Requires a
              latitude dimension to be present in the corresponding
              `~iris.cube.Cube`.

            * *None* : Equal weights for all grid points (*'none'* is
              also accepted).

             Alternatively a sequence of arrays of weights whose shapes
             are compatible with the corresponding `~iris.cube.Cube`
             instances in *datasets* may be supplied instead of
             specifying a weighting method.

        *center*
            If *True*, the mean along the first axis of each cube in
            *datasets* (the time-mean) will be removed prior to
            analysis. If *False*, the mean along the first axis will not
            be removed. Defaults to *True* (mean is removed).

            The covariance interpretation relies on the input data being
            anomalies with a time-mean of 0. Therefore this option
            should usually be set to *True*. Setting this option to
            *True* has the useful side effect of propagating missing
            values along the time dimension, ensuring that a solution
            can be found even if missing values occur in different
            locations at different times.

        *ddof*
            'Delta degrees of freedom'. The divisor used to normalize
            the covariance matrix is *N - ddof* where *N* is the
            number of samples. Defaults to *1*.

        **Returns:**

        *solver*
            An `MultivariateEof` instance.

        **Examples:**

        EOF analysis of two cubes with area-weighting::

            from eofs.multivariate.iris import MultivariateEof
            solver = MultivariateEof(cube1, cube2, weights='area')

        """
        # Record the number of input cubes.
        self._ncubes = len(cubes)
        # Check that the weights argument is valid and refactor it if there
        # is only one option provided.
        if weights in (None, 'area', 'coslat'):
            weights = [weights] * self._ncubes
        elif len(weights) != self._ncubes:
            raise ValueError('number of weights and cubes must match')
        # Process each input cube recording its time dimension coordinate,
        # other dimension coordinates, and defining its weight array.
        self._time = []
        self._coords = []
        passweights = []
        for cube, weight in zip(cubes, weights):
            if type(cube) is not Cube:
                raise TypeError('input is not an iris cube')
            # Record the time dimension and it's position. If its position is
            # not 0 then raise an error.
            time, time_dim = coord_and_dim(cube, 'time')
            if time_dim != 0:
                raise ValueError('time must be the first dimension, '
                                 'consider using the transpose() method')
            self._time.append(time)
            # Make a list of the cube's other dimension coordinates.
            coords = list(copy(cube.dim_coords))
            coords.remove(time)
            if len(coords) < 1:
                raise ValueError('one or more non-time '
                                 'dimensions are required')
            self._coords.append(coords)
            # Determine the weighting option for the cube.
            if weight is None:
                wtarray = None
            else:
                try:
                    scheme = weight.lower()
                    wtarray = weights_array(cube, scheme=scheme)
                except AttributeError:
                    wtarray = weight
            try:
                wtarray = wtarray.astype(cube.data.dtype)
            except AttributeError:
                pass
            passweights.append(wtarray)
        # Create a solver.
        self._solver = standard.MultivariateEof(
            [cube.data for cube in cubes],
            weights=passweights,
            center=center,
            ddof=ddof)
        #: Number of EOFs in the solution.
        self.neofs = self._solver.neofs
        # Names of the cubes.
        self._cube_names = map(
            lambda c: c.name(default='dataset').replace(' ', '_'), cubes)
        self._cube_var_names = [cube.var_name for cube in cubes]
Пример #4
0
    def __init__(self, cubes, weights=None, center=True, ddof=1):
        """Create a MultivariateEof instance.

        The EOF solution is computed at initialization time. Method
        calls are used to retrieve computed quantities.

        **Arguments:**

        *cubes*
            A list/tuple containing one or more `~iris.cube.Cube`
            instances, each with two or more dimensions, containing the
            data to be analysed. Time must be the first dimension of
            each `~iris.cube.Cube`. Missing values are allowed provided
            that they are constant with time in each field (e.g., values
            of an oceanographic field over land).

        **Optional arguments:**

        *weights*
            Sets the weighting method. One method can be chosen to apply
            to all cubes in *datasets* or a sequence of options can be
            given to specify a different weighting method for each cube
            in *datasets*. The following pre-defined weighting methods
            are available:

            * *'area'* : Square-root of grid cell area normalized by
              total grid area. Requires a latitude-longitude grid to be
              present in the corresponding `~iris.cube.Cube`. This is a
              fairly standard weighting strategy. If you are unsure
              which method to use and you have gridded data then this
              should be your first choice.

            * *'coslat'* : Square-root of cosine of latitude. Requires a
              latitude dimension to be present in the corresponding
              `~iris.cube.Cube`.

            * *None* : Equal weights for all grid points (*'none'* is
              also accepted).

             Alternatively a sequence of arrays of weights whose shapes
             are compatible with the corresponding `~iris.cube.Cube`
             instances in *datasets* may be supplied instead of
             specifying a weighting method.

        *center*
            If *True*, the mean along the first axis of each cube in
            *datasets* (the time-mean) will be removed prior to
            analysis. If *False*, the mean along the first axis will not
            be removed. Defaults to *True* (mean is removed).

            The covariance interpretation relies on the input data being
            anomalies with a time-mean of 0. Therefore this option
            should usually be set to *True*. Setting this option to
            *True* has the useful side effect of propagating missing
            values along the time dimension, ensuring that a solution
            can be found even if missing values occur in different
            locations at different times.

        *ddof*
            'Delta degrees of freedom'. The divisor used to normalize
            the covariance matrix is *N - ddof* where *N* is the
        Reconstruct the input field using EOFs 1, 2 and 5::

            reconstruction = solver.reconstuctedField([1, 2, 5])
            number of samples. Defaults to *1*.

        **Returns:**

        *solver*
            An `MultivariateEof` instance.

        **Examples:**

        EOF analysis of two cubes with area-weighting::

            from eofs.multivariate.iris import MultivariateEof
            solver = MultivariateEof(cube1, cube2, weights='area')

        """
        # Record the number of input cubes.
        self._ncubes = len(cubes)
        # Check that the weights argument is valid and refactor it if there
        # is only one option provided.
        if weights in (None, 'area', 'coslat'):
            weights = [weights] * self._ncubes
        elif len(weights) != self._ncubes:
            raise ValueError('number of weights and cubes must match')
        # Process each input cube recording its time dimension coordinate,
        # other dimension coordinates, and defining its weight array.
        self._time = []
        self._coords = []
        self._time_aux_coords = []
        self._space_aux_coords = []
        self._time_space_aux_coords = []
        passweights = []
        for cube, weight in zip(cubes, weights):
            if not isinstance(cube, Cube):
                raise TypeError('input is not an iris cube')
            # Record the time dimension and it's position. If its position is
            # not 0 then raise an error.
            time, time_dim = coord_and_dim(cube, 'time')
            if time_dim != 0:
                raise ValueError('time must be the first dimension, '
                                 'consider using the transpose() method')
            self._time.append(copy(time))
            # Make a list of the cube's other dimension coordinates.
            coords = [copy(coord) for coord in cube.dim_coords]
            coords.remove(time)
            if not coords:
                raise ValueError('one or more non-time '
                                 'dimensions are required')
            self._coords.append(coords)
            # Make a lists of the AuxCoords on the current cube and store
            # them for reapplication later.
            _t, _s, _ts = classified_aux_coords(cube)
            self._time_aux_coords.append(_t)
            self._space_aux_coords.append(_s)
            self._time_space_aux_coords.append(_ts)
            # Determine the weighting option for the cube.
            if weight is None:
                wtarray = None
            else:
                try:
                    scheme = weight.lower()
                    wtarray = weights_array(cube, scheme=scheme)
                except AttributeError:
                    wtarray = weight
            try:
                wtarray = wtarray.astype(cube.data.dtype)
            except AttributeError:
                pass
            passweights.append(wtarray)
        # Get a list of all the auxiliary coordinates that span just time
        # and are present on every input cube.
        self._common_time_aux_coords = common_items(self._time_aux_coords)
        # Create a solver.
        self._solver = standard.MultivariateEof([cube.data for cube in cubes],
                                                weights=passweights,
                                                center=center,
                                                ddof=ddof)
        #: Number of EOFs in the solution.
        self.neofs = self._solver.neofs
        # Names of the cubes.
        self._cube_names = map(
            lambda c: c.name(default='dataset').replace(' ', '_'), cubes)
        self._cube_var_names = [cube.var_name for cube in cubes]