.. _reducing_a_cube: ================ Reducing a Cube ================ The :doc:`loading_iris_cubes` section of the user guide showed how to load data into multidimensional Iris cubes. However it is often necessary to reduce the dimensionality of a cube down to something more appropriate. Iris provides several ways of reducing both the amount of data and/or the number of dimensions in your cube depending on the circumstance. In all cases **the subset of a valid cube is itself a valid cube**. Cube extraction ^^^^^^^^^^^^^^^^ A subset of a cube can be "extracted" from a multi-dimensional cube in order to reduce its dimensionality:: filename = iris.sample_data_path('hybrid_height.pp') cube = iris.load_strict(filename) print cube equator_slice = cube.extract(iris.Constraint(grid_latitude=0)) print equator_slice In this example we start with a 3 dimensional cube, with dimensions of ``height``, ``latitude`` and ``longitude``, and extract every point where the latitude is 0, resulting in a 2d cube with axes of ``height`` and ``longitude``. .. warning:: Caution is required when using equality constraints with floating point coordinates such as ``latitude``. Printing the points of a coordinate does not necessarily show the full precision of the underlying number and it is very easy return no matches to a constraint when one was expected. This can be avoided by using a function as the argument to the constraint:: def near_zero(cell): """Returns true if the cell is between -0.1 and 0.1.""" return -0.1 < cell < 0.1 equator_constraint = iris.Constraint(grid_latitude=near_zero) Often you will see this construct in shorthand using a lambda function definition:: equator_constraint = iris.Constraint(grid_latitude=lambda cell: -0.1 < cell < 0.1) The extract method could be applied again to the *equator_slice* cube to get a further subset. For example to get a ``model_level_number`` of 10 at the equator the following line extends the previous example:: equator_model_level_10_slice = equator_slice.extract(iris.Constraint(model_level_number=10)) print equator_model_level_10_slice The two steps required to get ``model_level_number`` of 10 at the equator can be simplified into a single constraint:: filename = iris.sample_data_path('PP', 'globClim1', 'theta.pp') cube = iris.load_strict(filename) equator_model_level_10_slice = cube.extract(iris.Constraint(grid_latitude=0, model_level_number=10)) print equator_model_level_10_slice As we saw in :doc:`loading_iris_cubes` the result of :func:`iris.load` is a :class:`CubeList `. The ``extract`` method also exists on a :class:`CubeList ` and behaves in exactly the same way as loading with constraints:: air_temp_and_fp_6 = iris.Constraint('air_potential_temperature', forecast_period=6) level_10 = iris.Constraint(model_level_number=10) filename = iris.sample_data_path('uk_hires.pp') cubes = iris.load(filename).extract(air_temp_and_fp_6 & level_10) print cubes Cube iteration ^^^^^^^^^^^^^^^ A useful way of dealing with a Cube in its **entirety** is by iterating over its layers or slices. For example, to deal with a 3 dimensional cube (z,y,x) you could iterate over all 2 dimensional slices in y and x which make up the full 3d cube.:: import iris filename = iris.sample_data_path('hybrid_height.nc') cube = iris.load_strict(filename) print cube for yx_slice in cube.slices(['grid_latitude', 'grid_longitude']): print repr(yx_slice) As the original cube had the shape (15, 100, 100) there were 15 latitude longitude slices and hence the line ``print repr(yx_slice)`` was run 15 times. .. note:: The order of latitude and longitude in the list is important; had they been swapped the resultant cube slices would have been transposed. For further information see :py:meth:`Cube.slices `. This method can handle n-dimensional slices by providing more or fewer coordinate names in the list to **slices**:: import iris filename = iris.sample_data_path('hybrid_height.nc') cube = iris.load_strict(filename) print cube for i, x_slice in enumerate(cube.slices(['grid_longitude'])): print i, repr(x_slice) The Python function :py:func:`enumerate` is used in this example to provide an incrementing variable **i** which is printed with the summary of each cube slice. Note that there were 1500 1d longitude cubes as a result of slicing the 3 dimensional cube (15, 100, 100) by longitude (i starts at 0 and 1500 = 15 * 100). .. hint:: It is often useful to get a single 2d slice from a multidimensional cube in order to develop a 2d plot function, for example. This can be achieved by using the ``next()`` method on the result of slices:: first_slice = cube.slices(['grid_latitude', 'grid_longitude']).next() Once the your code can handle a 2d slice, it is then an easy step to loop over **all** 2d slices within the bigger cube using the slices method. Cube indexing ^^^^^^^^^^^^^ In the same way that you would expect a numeric multidimensional array to be **indexed** to take a subset of your original array, you can **index** a Cube for the same purpose. Here are some examples of array indexing in :py:mod:`numpy`:: import numpy # create an array of 12 consecutive integers starting from 0 a = numpy.arange(12) print a print a[0] # first element of the array print a[-1] # last element of the array print a[0:4] # first four elements of the array (this is the same as a[:4]) print a[-4:] # last four elements of the array print a[::-1] # gives all of the array, but backwards # Make a 2d array by reshaping a b = a.reshape(3, 4) print b print b[0, 0] # first element of the first and second dimensions print b[0] # first element of the first dimension (+ every other dimension) # get the second element of the first dimension and all of the second dimension # in reverse, by steps of two. print b[1, ::-2] Similarly, Iris cubes have indexing capability:: import iris filename = iris.sample_data_path('hybrid_height.nc') cube = iris.load_strict(filename) print cube # get the first element of the first dimension (+ every other dimension) print cube[0] # get the last element of the first dimension (+ every other dimension) print cube[-1] # get the first 4 elements of the first dimension (+ every other dimension) print cube[0:4] # Get the first element of the first and third dimension (+ every other dimension) print cube[0, :, 0] # Get the second element of the first dimension and all of the second dimension # in reverse, by steps of two. print cube[1, ::-2]