cf.Field.subspace

Field.subspace

Create a subspace of the field.

A subspace may be defined in “metadata-space” via data array values of its domain items: dimension coordinate, auxiliary coordinate, cell measure, domain ancillary and field ancillary objects.

Alternatively, a subspace my be defined be in “index-space” via explicit indices for the data array using an extended Python slicing syntax.

Defining a subspace in metadata-space

Defining a subspace in metadata-space has the following features

  • Axes to be subspaced may identified by metadata, rather than their position in the data array.
  • The position in the data array of each axis need not be known and the axes to be subspaced may be given in any order.
  • Axes for which no subspacing is required need not be specified.
  • Size one axes of the domain which are not spanned by the data array may be specified.
>>> f
<CF Field: air_temperature(time(12), latitude(73), longitude(96)) K>
>>> f.subspace(latitude=0)
<CF Field: air_temperature(time(12), latitude(1), longitude(96)) K>
>>> f.subspace(longitude=cf.ge(180), latitude=0)
<CF Field: air_temperature(time(12), latitude(1), longitude(48)) K>
>>> f.subspace(longitude=cf.ge(180), latitude=0, time=cf.month(cf.lt(8)))
<CF Field: air_temperature(time(7), latitude(1), longitude(48)) K>
  • The field may be subspaced according to conditions on multidimensional items.
>>> print f
eastward_wind field summary
---------------------------
Data           : eastward_wind(time(3), air_pressure(5), grid_latitude(110), grid_longitude(106)) m s-1
Cell methods   : time: mean
Axes           : time(3) = [1979-05-01T12:00:00Z, ..., 1979-05-03T12:00:00Z] gregorian
               : air_pressure(5) = [850.0, ..., 50.0] hPa
               : grid_latitude(110) = [23.32, ..., -24.64] degrees
               : grid_longitude(106) = [-20.54, ..., 25.66] degrees
Aux coords     : latitude(grid_latitude(110), grid_longitude(106)) = [[67.12, ..., 22.89]] degrees_north
               : longitude(grid_latitude(110), grid_longitude(106)) = [[-45.98, ..., 35.29]] degrees_east
Coord refs     : rotated_latitude_longitude
>>> f.subspace(latitude=cf.gt(60))
<CF Field: eastward_wind(time(3), air_pressure(5), grid_latitude(35), grid_longitude(106)) m s-1>
>>> f.subspace(longitude=cf.wi(-30, 30), latitude=cf.gt(60))
<CF Field: eastward_wind(time(3), air_pressure(5), grid_latitude(35), grid_longitude(66)) m s-1>
  • Three types of subspace are available:

    • A “compress” subspace (the default) is the smallest possible subspace which contains the selected elements. As many non-selected elements are discarded as possible, meaning that the subspace may not form a contiguous bloack of te original field.
    • An “envelope” subspace is the smallest subspace which contains the selected elements and forms a contiguous block of the original field. Interior, non-selected elements are set to missing data values.
    • A “full” subspace is is the same size as the original field, but with all non-selected elements set to missing data values.
    >>> f
    <CF Field: eastward_wind(time(3), air_pressure(5), grid_latitude(110), grid_longitude(106)) m s-1>
    >>> f.subspace(grid_longitude=cf.wi(-10, -5) | cf.wi(5, 10))
    <CF Field: eastward_wind(time(3), air_pressure(5), grid_latitude(110), grid_longitude(23)) m s-1>
    >>> f.subspace('compress', grid_longitude=cf.wi(-10, -5) | cf.wi(5, 10))
    <CF Field: eastward_wind(time(3), air_pressure(5), grid_latitude(110), grid_longitude(23)) m s-1>
    
    >>> f.subspace('envelope', grid_longitude=cf.wi(-10, -5) | cf.wi(5, 10))
    <CF Field: eastward_wind(time(3), air_pressure(5), grid_latitude(110), grid_longitude(46)) m s-1>
    
    >>> f.subspace('full', grid_longitude=cf.wi(-10, -5) | cf.wi(5, 10))
    <CF Field: eastward_wind(time(3), air_pressure(5), grid_latitude(110), grid_longitude(106)) m s-1>
    

    This is also works for subspaces defined by multidimensional curvilinear, or unstructured coordinates:

    >>> print f.subspace('envelope', longitude=cf.wi(-10, 30))
    eastward_wind field summary
    ---------------------------
    Data           : eastward_wind(time(3), air_pressure(5), grid_latitude(110), grid_longitude(91)) m s-1
    Cell methods   : time: mean
    Axes           : time(3) = [1979-05-01T12:00:00Z, ..., 1979-05-03T12:00:00Z] gregorian
                   : air_pressure(5) = [850.000061035, ..., 50.0000038147] hPa
                   : grid_latitude(110) = [23.3200002313, ..., -24.6399995089] degrees
                   : grid_longitude(91) = [-19.6600109935, ..., 19.9399887919] degrees
    Aux coords     : latitude(grid_latitude(110), grid_longitude(91)) = [[67.6943495635, ..., 24.6226629697]] degrees_north
                   : longitude(grid_latitude(110), grid_longitude(91)) = [[-44.4891982306, ..., 29.9371053319]] degrees_east
    Coord refs     : rotated_latitude_longitude
    

Defining a subspace in index-space

Subspacing in index-space uses an extended Python slicing syntax, which is similar to numpy array indexing. Extensions to the numpy indexing functionality are:

  • The indices for each axis work independently. When more than one axis’s slice is a 1-d boolean sequence or 1-d sequence of integers, then these indices work independently along each axis (similar to the way vector subscripts work in Fortran), rather than by their elements.
  • Boolean indices may be any object which exposes the numpy array interface, such as the field’s coordinate objects.
>>> f
<CF Field: air_temperature(time(12), latitude(73), longitude(96)) K>
>>> f.subspace[:, [0, 72], [5, 4, 3]]
<CF Field: air_temperature(time(12), latitude(2), longitude(3)) K>
>>> f.subspace[:, f.coord('latitude')<0]
<CF Field: air_temperature(time(12), latitude(36), longitude(96)) K>

Size one axes

Size one axes in the subspaced field are always retained, but may be subsequently removed with the field’s squeeze method:

>>> f
<CF Field: air_temperature(time(12), latitude(73), longitude(96)) K>
>>> f.subspace(latitude=0)
<CF Field: air_temperature(time(12), latitude(1), longitude(96)) K>
>>> f.subspace(latitude=0).squeeze()
<CF Field: air_temperature(time(12), longitude(96)) K>
>>> f.subspace[..., 12:13]
<CF Field: air_temperature(time(12), latitude(73), longitude(1)) K>
>>> f.subspace[..., -1].squeeze(2)
<CF Field: air_temperature(time(12), latitude(73)) K>

Assignment to the data array

A subspace defined in index-space may have its data array values changed by simple assignment:

>>> f
<CF Field: air_temperature(time(12), latitude(73), longitude(96)) K>
>>> f.subspace[0:6] = f.subspace[6:12]
>>> f.subspace[..., 0:48] = -99

To assign to a subspace defined in metadata-space, the equivalent index-space indices must first be found with the field’s indices method, and then the assignment may be applied in index-space:

>>> index = f.indices(longitude=cf.lt(180))
>>> f.subspace[index] = cf.masked

This example could have been carried out in index-space if the longitude coordinates were 1-d and their position in the data array was known:

>>> f.subspace[:, :, f.coord('longitude')<180] = cf.masked

However, if the longitude coordinates were multidimensional then only the indices approach would have been possible.

Examples:
>>> f,shape
(12, 73, 96)
>>> f.subspace().shape
(12, 73, 96)
>>> f.subspace(latitude=0).shape
(12, 1, 96)
>>> f.subspace(latitude=cf.wi(-30, 30)).shape
(12, 25, 96)
>>> f.subspace(long=cf.ge(270, 'degrees_east'), lat=cf.set([0, 2.5, 10])).shape
(12, 3, 24)
>>> f.subspace(latitude=cf.lt(0, 'degrees_north'))
(12, 36, 96)
>>> f.subspace(latitude=[cf.lt(0, 'degrees_north'), 90])
(12, 37, 96)
>>> import math
>>> f.subspace('exact', longitude=cf.lt(math.pi, 'radian'), height=2)
(12, 73, 48)
>>> f.subspace(height=cf.gt(3))
IndexError: No indices found for 'height' values gt 3
>>> f.subspace(dim2=3.75).shape
(12, 1, 96)
>>> f.subspace(time=cf.le(cf.dt('1860-06-16 12:00:00')).shape
(6, 73, 96)
>>> f.subspace(time=cf.gt(cf.dt(1860, 7)),shape
(5, 73, 96)

Note that if a comparison function (such as cf.wi) does not specify any units, then the units of the named item are assumed.