In [ ]:
%matplotlib inline
Learning outcome: by the end of this section, you will be able to apply Iris functionality to combine multiple Iris cubes into a new larger cube.
Duration: 30 minutes
Overview:
4.1 Merge
4.2 Concatenate
4.3 Exercise
4.4 Summary of the Section
In [ ]:
import iris
import numpy as np
When Iris loads data it tries to reduce the number of cubes returned by collecting together multiple fields with shared metadata into a single multidimensional cube. In Iris, this is known as merging.
In order to merge two cubes, they must be identical in everything but a scalar dimension, which goes on to become a new data dimension.
The diagram below shows how three 2D cubes, which have the same x and y coordinates but different z coordinates, are merged together to create a single 3D cube.
The iris.load_raw
function can be used as a diagnostic tool to load the individual "fields" that Iris identifies in a given set of filenames before any merge takes place.
Let's compare the behaviour of iris.load_raw
and the behaviour of the general purpose loading function, iris.load
First, we load in a file using iris.load
:
In [ ]:
fname = iris.sample_data_path('GloSea4', 'ensemble_008.pp')
cubes = iris.load(fname)
print(cubes)
As you can see iris.load
returns a CubeList containing a single 3D cube.
Now let's try loading in the file using iris.load_raw
:
In [ ]:
fname = iris.sample_data_path('GloSea4', 'ensemble_008.pp')
raw_cubes = iris.load_raw(fname)
print(raw_cubes)
This time, iris has returned six 2D cubes.
PP files usually contain multiple 2D fields. iris.load_raw
has returned a 2D cube for each of these fields, whereas iris.load
has merged the cubes together then returned the resulting 3D cube.
When we look in detail at the raw 2D cubes, we find that they are identical in every coordinate except for the scalar forecast_period and time coordinates:
In [ ]:
print(raw_cubes[0])
print('--' * 40)
print(raw_cubes[1])
To merge a CubeList, we can use the merge
or merge_cube
methods.
The merge
method will try to merge together the cubes in the CubeList in order to return a CubeList of as few cubes as possible.
The merge_cube
method will do the same as merge
but will return a single Cube. If the initial CubeList cannot be merged into a single Cube, merge_cube
will raise an error, giving a helpful message explaining why the cubes cannot be merged.
Let's merge the raw 2D cubes we previously loaded in:
In [ ]:
merged_cubelist = raw_cubes.merge()
print(merged_cubelist)
merge
has returned a cubelist of a single 3D cube.
In [ ]:
merged_cube = merged_cubelist[0]
print(merged_cube)
Try merging raw_cubes using the merge_cube method.
In [ ]:
#
# edit space for user code ...
#
When we look in more detail at our merged cube, we can see that the time coordinate has become a new dimension, as well as gaining another forecast_period auxiliary coordinate:
In [ ]:
print(merged_cube.coord('time'))
print(merged_cube.coord('forecast_period'))
In order to avoid the Iris merge functionality making inappropriate assumptions about the data, merge is strict with regards to the uniformity of the incoming cubes.
For example, if we load the fields from two ensemble members from the GloSea4 model sample data, we see we have 12 fields before any merge takes place:
In [ ]:
fname = iris.sample_data_path('GloSea4', 'ensemble_00[34].pp')
cubes = iris.load_raw(fname, 'surface_temperature')
print(len(cubes))
If we try to merge these 12 cubes we get 2 cubes rather than one:
In [ ]:
incomplete_cubes = cubes.merge()
print(incomplete_cubes)
When we look in more detail at these two cubes, what is different between the two? (Hint: One value changes, another is completely missing)
In [ ]:
print(incomplete_cubes[0])
print('--' * 40)
print(incomplete_cubes[1])
As mentioned earlier, if merge_cube
cannot merge the given CubeList to return a single Cube, it will raise a helpful error message identifying the cause of the failiure.
Try merging the loaded cubes using merge_cube rather than merge.
In [ ]:
#
# edit space for user code ...
#
By inspecting the cubes themselves or using the error message raised when using merge_cube
we can see that some cubes are missing the realization
coordinate.
By adding the missing coordinate, we can trigger a merge of the 12 cubes into a single cube, as expected:
In [ ]:
for cube in cubes:
if not cube.coords('realization'):
cube.add_aux_coord(iris.coords.DimCoord(np.int32(3),
'realization'))
merged_cube = cubes.merge_cube()
print(merged_cube)
We have seen that merge combines a list of cubes with a common scalar coordinate to produce a single cube with a new dimension created from these scalar values.
But what happens if you try to combine cubes along a common dimension.
Let's create a CubeList with two cubes that have been indexed along the time dimension of the original cube.
In [ ]:
fname = iris.sample_data_path('A1B_north_america.nc')
cube = iris.load_cube(fname)
cube_1 = cube[:10]
cube_2 = cube[10:20]
cubes = iris.cube.CubeList([cube_1, cube_2])
print(cubes)
These cubes should be able to be joined together; after all, they have both come from the same original cube!
However, merge
returns two cubes, suggesting that these two cubes cannot be merged:
In [ ]:
print(cubes.merge())
Merge cannot be used to combine common non-scalar coordinates. Instead we must use concatenate.
Concatenate joins together ("concatenates") common non-scalar coordinates to produce a single cube with the common dimension extended.
In the below diagram, we see how three 3D cubes are concatenated together to produce a 3D cube with an extended t
dimension.
To concatenate a CubeList, we can use the concatenate
or concatenate_cube
methods.
Similar to merging, concatenate
will return a CubeList of as few cubes as possible, whereas concatenate_cube
will attempt to return a cube, raising an error with a helpful message where this is not possible.
If we apply concatenate
to our cubelist, we will see that it returns a CubeList with a single Cube:
In [ ]:
print(cubes.concatenate())
Try concatenating cubes using the concatenate_cube method.
In [ ]:
#
# edit space for user code ...
#
a) Use iris.load_raw
to load in the air_potential_temperature
cubes from the files 'resources/merge_exercise.1.*.nc'
. Store the cubes in a variable called raw_cubes
.
Hint: Constraints can be given to the load_raw
function as you would with the other load functions.
In [ ]:
# EDIT for user code ...
In [ ]:
# SAMPLE SOLUTION : Un-comment and execute the following to see a possible solution ...
# %load solutions/iris_exercise_4.3.1a
b) Try merging the loaded cubes into a single cube. Why does this raise an error?
In [ ]:
# user code ...
In [ ]:
# SAMPLE SOLUTION
# %load solutions/iris_exercise_4.3.1b
c) Fix the cubes such that they can be merged into a single cube.
Hint: You can use del
to remove an item from a dictionary.
In [ ]:
# user code ...
In [ ]:
# SAMPLE SOLUTION
# %load solutions/iris_exercise_4.3.1c
a) Use iris.load_raw
to load in the air_potential_temperature
cubes from the files 'resources/merge_exercise.5.*.nc'
. Store the cubes in a variable called raw_cubes
.
In [ ]:
# user code ...
In [ ]:
# SAMPLE SOLUTION
# %load solutions/iris_exercise_4.3.2a
b) Join the cubes together into a single cube. Should these cubes be merged or concatenated?
In [ ]:
# user code ...
In [ ]:
# SAMPLE SOLUTION
# %load solutions/iris_exercise_4.3.2b
In this section we learnt: