44Merge and Concatenate
55=====================
66
7- We saw in the :doc: `loading_iris_cubes ` section that Iris tries to load as few cubes as
7+ We saw in the :doc: `loading_iris_cubes ` chapter that Iris tries to load as few cubes as
88possible. This is done by collecting together multiple fields with a shared standard
99name (and other key metadata) into a single multidimensional cube. The processes that
1010perform this behaviour in Iris are known as ``merge `` and ``concatenate ``.
1111
12- This section describes the ``merge `` and ``concatenate `` processes; it explains
12+ This chapter describes the ``merge `` and ``concatenate `` processes; it explains
1313why common issues occur when using them and gives advice on how prevent these
1414issues from occurring.
1515
@@ -32,12 +32,12 @@ sequential dimension coordinates*.
3232
3333Let's imagine 28 individual cubes representing the
3434temperature at a location ``(y, x) ``, one cube for each day of February. We can use
35- :meth: `merge < iris.cube.CubeList.merge> ` to combine the 28 ``(y, x) `` cubes into
35+ :meth: `~ iris.cube.CubeList.merge ` to combine the 28 ``(y, x) `` cubes into
3636a single ``(t, y, x) `` cube, where the length of the ``t `` dimension is 28.
3737
3838Now imagine 12 individual cubes representing daily temperature at a time and
3939location ``(t, y, x) ``, one cube for each month in the year. We can use
40- :meth: `concatenate < iris.cube.CubeList.concatenate> ` to combine the 12
40+ :meth: `~ iris.cube.CubeList.concatenate ` to combine the 12
4141``(t, y, x) `` cubes into a single ``(t, y, x) `` cube, where the length
4242of the ``t `` dimension is now 365.
4343
@@ -59,7 +59,7 @@ if the scalar coordinate sequences form an orthogonal basis.
5959.. important ::
6060
6161 The shape, metadata, attributes, coordinates, coordinates metadata, fill value and
62- other aspects of the a cube must be consistent across all of the input cubes.
62+ other aspects of the input cubes must be consistent across all of the input cubes.
6363
6464 The ``merge `` process will fail if these are not consistent. Such failures are
6565 covered in the :ref: `merge_concat_common_issues ` section.
@@ -77,7 +77,7 @@ The :meth:`CubeList.merge <iris.cube.CubeList.merge>` method operates on a list
7777of cubes and returns a new :class: `~iris.cube.CubeList ` containing the cubes
7878that have been merged.
7979
80- .. testsetup ::
80+ .. testsetup :: merge
8181
8282 import numpy as np
8383 import iris
@@ -96,7 +96,7 @@ variable called ``cubes``, each with a scalar ``z`` coordinate of
9696differing value. We can merge these cubes by stacking the scalar ``z `` coordinates to
9797make a new ``z `` dimension coordinate:
9898
99- .. doctest ::
99+ .. doctest :: merge
100100 :options: +ELLIPSIS, +NORMALIZE_WHITESPACE
101101
102102 >>> print cubes
@@ -166,7 +166,6 @@ into a single cube:
166166 cubes = iris.cube.CubeList([_xy_cube(1), _xy_cube(2), _xy_cube(3)])
167167 cubes[0].attributes['Conventions'] = 'CF-1.5'
168168
169-
170169.. doctest :: merge_vs_merge_cube
171170 :options: +ELLIPSIS, +NORMALIZE_WHITESPACE
172171
@@ -177,9 +176,9 @@ into a single cube:
177176
178177 >>> print cubes[0 ].attributes
179178 {'Conventions': 'CF-1.5'}
180- >>> print cubes[1 ]
179+ >>> print cubes[1 ].attributes
181180 {}
182- >>> print cubes[2 ]
181+ >>> print cubes[2 ].attributes
183182 {}
184183
185184 >>> print cubes.merge()
@@ -220,7 +219,6 @@ load cubes from files without the ``merge`` process taking place. The return val
220219:func: `iris.load_raw ` is always a :class: `~iris.cube.CubeList ` instance.
221220
222221
223-
224222Concatenate
225223-----------
226224
@@ -236,7 +234,7 @@ The order of the input cubes does not affect the ``concatenate`` process.
236234.. important ::
237235
238236 The shape, metadata, attributes, coordinates, coordinates metadata, fill value and
239- other aspects of the a cube must be consistent across all of the input cubes.
237+ other aspects of the input cubes must be consistent across all of the input cubes.
240238
241239 The ``concatenate `` process will fail if these are not consistent. Such failures are
242240 covered in the :ref: `merge_concat_common_issues ` section.
@@ -258,7 +256,22 @@ Let's have a look at the :meth:`~iris.cube.CubeList.concatenate` method in opera
258256In the example below we have three 3D (*x *, *y *, *t *) cubes whose ``t `` coordinates
259257have sequentially increasing ranges.
260258These cubes can be concatenated by combining the ``t `` coordinates of the input
261- cubes to form a new cube with an extended ``t `` coordinate::
259+ cubes to form a new cube with an extended ``t `` coordinate:
260+
261+ .. testsetup :: concatenate
262+
263+ import numpy as np
264+ import iris
265+ def _xyt_cube(t):
266+ cube = iris.cube.Cube(np.arange(12 * len(t)).reshape(-1, 3, 4), 'air_temperature', units='kelvin')
267+ cube.add_dim_coord(iris.coords.DimCoord(range(3), long_name='y'), 1)
268+ cube.add_dim_coord(iris.coords.DimCoord(range(4), long_name='x'), 2)
269+ cube.add_dim_coord(iris.coords.DimCoord(t, long_name='t'), 0)
270+ return cube
271+ cubes = iris.cube.CubeList([_xyt_cube(np.arange(31)), _xyt_cube(np.arange(28) + 31), _xyt_cube(np.arange(31) + 59)])
272+
273+ .. doctest :: concatenate
274+ :options: +ELLIPSIS, +NORMALIZE_WHITESPACE
262275
263276 >>> print cubes
264277 0: air_temperature / (kelvin) (t: 31; y: 3; x: 4)
@@ -269,25 +282,6 @@ cubes to form a new cube with an extended ``t`` coordinate::
269282 0: air_temperature / (kelvin) (t: 90; y: 3; x: 4)
270283
271284
272- ..
273-
274- DOCTEST DISABLED TEMPORARILY
275- .. testsetup :: concatenate
276-
277- import numpy as np
278- import iris
279- def _xyt_cube(t):
280- cube = iris.cube.Cube(np.arange(12 * len(t)).reshape(-1, 3, 4), 'air_temperature', units='kelvin')
281- cube.add_dim_coord(iris.coords.DimCoord(range(3), long_name='y'), 1)
282- cube.add_dim_coord(iris.coords.DimCoord(range(4), long_name='x'), 2)
283- cube.add_dim_coord(iris.coords.DimCoord(t, long_name='t'), 0)
284- return cube
285- cubes = iris.cube.CubeList([_xyt_cube(range(31)), _xyt_cube(range(28)), _xyt_cube(range(31))])
286-
287- .. doctest :: concatenate
288- :options: +ELLIPSIS, +NORMALIZE_WHITESPACE
289-
290-
291285The following diagram illustrates what has taken place in this example:
292286
293287.. image :: concat.svg
@@ -317,8 +311,23 @@ from the earlier merge example.
317311For the purposes of this example we'll add a *History * attribute to the first
318312cube's :data: `~iris.cube.Cube.attributes ` dictionary.
319313Remember that the attributes *must * be consistent across all cubes in order to
320- concatenate into a single cube::
314+ concatenate into a single cube:
321315
316+ .. testsetup :: concatenate_vs_concatenate_cube
317+
318+ import numpy as np
319+ import iris
320+ def _xyt_cube(t):
321+ cube = iris.cube.Cube(np.arange(12 * len(t)).reshape(-1, 3, 4), 'air_temperature', units='kelvin')
322+ cube.add_dim_coord(iris.coords.DimCoord(range(3), long_name='y'), 1)
323+ cube.add_dim_coord(iris.coords.DimCoord(range(4), long_name='x'), 2)
324+ cube.add_dim_coord(iris.coords.DimCoord(t, long_name='t'), 0)
325+ return cube
326+ cubes = iris.cube.CubeList([_xyt_cube(np.arange(31)), _xyt_cube(np.arange(28) + 31), _xyt_cube(np.arange(31) + 59)])
327+ cubes[0].attributes['History'] = 'Created 2010-06-30'
328+
329+ .. doctest :: concatenate_vs_concatenate_cube
330+ :options: +ELLIPSIS, +NORMALIZE_WHITESPACE
322331
323332 >>> print cubes
324333 0: air_temperature / (kelvin) (t: 31; y: 3; x: 4)
@@ -331,8 +340,8 @@ concatenate into a single cube::
331340 {}
332341
333342 >>> print cubes.concatenate()
334- 0: air_temperature / (kelvin) (t: 31; x: 10; y: 10 )
335- 1: air_temperature / (kelvin) (t: 59; x: 10; y: 10 )
343+ 0: air_temperature / (kelvin) (t: 31; y: 3; x: 4 )
344+ 1: air_temperature / (kelvin) (t: 59; y: 3; x: 4 )
336345 >>> print cubes.concatenate_cube()
337346 Traceback (most recent call last):
338347 ...
@@ -341,29 +350,6 @@ concatenate into a single cube::
341350 Cube metadata differs for phenomenon: air_temperature
342351
343352
344- ..
345-
346- DOCTEST DISABLED TEMPORARILY
347-
348- .. testsetup :: concatenate_vs_concatenate_cube
349-
350- import numpy as np
351- import iris
352- def _xyt_cube(t):
353- cube = iris.cube.Cube(np.arange(12 * len(t)).reshape(-1, 3, 4), 'air_temperature', units='kelvin')
354- cube.add_dim_coord(iris.coords.DimCoord(range(3), long_name='y'), 1)
355- cube.add_dim_coord(iris.coords.DimCoord(range(4), long_name='x'), 2)
356- cube.add_dim_coord(iris.coords.DimCoord(t, long_name='t'), 0)
357- return cube
358- cubes = iris.cube.CubeList([_xyt_cube(range(31)), _xyt_cube(range(28)), _xyt_cube(range(31))])
359- cubes[0].attributes['History'] = 'Created 2010-06-30'
360-
361-
362- .. doctest :: concatenate_vs_concatenate_cube
363- :options: +ELLIPSIS, +NORMALIZE_WHITESPACE
364-
365-
366-
367353Note that :meth: `~iris.cube.CubeList.concatenate ` returns two cubes here.
368354All the cubes that can be concatenated have been. Any cubes that can't be concatenated are
369355included unchanged in the returned :class: `~iris.cube.CubeList `.
@@ -379,22 +365,22 @@ single cube. An example of fixing an issue like this can be found in the
379365Common issues with merge and concatenate
380366----------------------------------------
381367
382- The Iris algorithms that drive :meth: `merge < iris.cube.CubeList.merge> ` and
383- :meth: `concatenate < iris.cube.CubeList.concatenate> ` are complex and depend
368+ The Iris algorithms that drive :meth: `~ iris.cube.CubeList.merge ` and
369+ :meth: `~ iris.cube.CubeList.concatenate ` are complex and depend
384370on a number of different elements of the input cubes being consistent across
385371all input cubes.
386372If this consistency is not maintained then the
387- :meth: `merge < iris.cube.CubeList.merge> ` or
388- :meth: `concatenate < iris.cube.CubeList.concatenate> ` process can fail in a
373+ :meth: `~ iris.cube.CubeList.merge ` or
374+ :meth: `~ iris.cube.CubeList.concatenate ` process can fail in a
389375seemingly arbitrary manner.
390376
391- The methods :meth: `merge_cube < iris.cube.CubeList.merge_cube> ` and
392- :meth: `concatenate_cube < iris.cube.CubeList.concatenate_cube> `
377+ The methods :meth: `~ iris.cube.CubeList.merge_cube ` and
378+ :meth: `~ iris.cube.CubeList.concatenate_cube `
393379were introduced to Iris to help you locate differences in input cubes
394380that prevent the input cubes merging or concatenating.
395381Nevertheless, certain difficulties with using
396- :meth: `merge < iris.cube.CubeList.merge> ` and
397- :meth: `concatenate < iris.cube.CubeList.concatenate> ` occur frequently.
382+ :meth: `~ iris.cube.CubeList.merge ` and
383+ :meth: `~ iris.cube.CubeList.concatenate ` occur frequently.
398384This section describes these common difficulties, why they arise and
399385what you can do to avoid them.
400386
@@ -423,8 +409,7 @@ To demonstrate using :func:`~iris.experimental.equalise_cubes.equalise_attribute
423409let's return to our non-merging list of input cubes from the merge_cube example
424410from earlier.
425411We'll call :func: `~iris.experimental.equalise_cubes.equalise_attributes ` on the
426- input cubes before merging the input cubes using :meth: `~iris.cube.CubeList.merge_cube `::
427-
412+ input cubes before merging the input cubes using :meth: `~iris.cube.CubeList.merge_cube `:
428413
429414.. doctest :: merge_vs_merge_cube
430415 :options: +ELLIPSIS, +NORMALIZE_WHITESPACE
@@ -437,9 +422,9 @@ input cubes before merging the input cubes using :meth:`~iris.cube.CubeList.merg
437422
438423 >>> print cubes[0 ].attributes
439424 {'Conventions': 'CF-1.5'}
440- >>> print cubes[1 ]
425+ >>> print cubes[1 ].attributes
441426 {}
442- >>> print cubes[2 ]
427+ >>> print cubes[2 ].attributes
443428 {}
444429
445430 >>> print cubes.merge_cube()
@@ -455,7 +440,11 @@ input cubes before merging the input cubes using :meth:`~iris.cube.CubeList.merg
455440 {}
456441
457442 >>> print cubes.merge_cube()
458- 0: air_temperature / (kelvin) (z: 3; y: 4; x: 5)
443+ air_temperature / (kelvin) (z: 3; y: 4; x: 5)
444+ Dimension coordinates:
445+ z x - -
446+ y - x -
447+ x - - x
459448
460449
461450**Incomplete Data **
@@ -495,21 +484,39 @@ scalar ``z`` coordinate with value 2 and the third has a scalar ``z``
495484coordinate with value 1.
496485The first and third cubes are thus identical.
497486We will demonstrate the effect of merging the input cubes with ``unique=False ``
498- (duplicate cubes allowed) and ``unique=True `` (duplicate cubes not allowed)::
487+ (duplicate cubes allowed) and ``unique=True `` (duplicate cubes not allowed):
488+
489+ .. testsetup :: merge_duplicate
490+
491+ import numpy as np
492+ import iris
493+ def _xy_cube(z):
494+ cube = iris.cube.Cube(np.arange(20).reshape(4, 5), 'air_temperature', units='kelvin')
495+ cube.add_dim_coord(iris.coords.DimCoord(range(4), long_name='y'), 0)
496+ cube.add_dim_coord(iris.coords.DimCoord(range(5), long_name='x'), 1)
497+ cube.add_aux_coord(iris.coords.DimCoord(z, long_name='z', units='meters'))
498+ return cube
499+ cubes = iris.cube.CubeList([_xy_cube(1), _xy_cube(2), _xy_cube(1)])
500+
501+ .. doctest :: merge_duplicate
502+ :options: +ELLIPSIS, +NORMALIZE_WHITESPACE
499503
500504 >>> print cubes
501- 0: air_temperature / (kelvin) (x: 10; y: 10)
502- 1: air_temperature / (kelvin) (x: 10; y: 10)
503- 2: air_temperature / (kelvin) (x: 10; y: 10)
505+ 0: air_temperature / (kelvin) (y: 4; x: 5)
506+ 1: air_temperature / (kelvin) (y: 4; x: 5)
507+ 2: air_temperature / (kelvin) (y: 4; x: 5)
508+
504509 >>> print cubes.merge(unique = False )
505- 0: air_temperature / (kelvin) (z: 2; x: 10; y: 10)
506- 1: air_temperature / (kelvin) (z: 2; x: 10; y: 10)
507- >>> print cubelist.merge() # unique=True is the default.
510+ 0: air_temperature / (kelvin) (z: 2; y: 4; x: 5)
511+ 1: air_temperature / (kelvin) (z: 2; y: 4; x: 5)
512+
513+ >>> print cubes.merge() # unique=True is the default.
508514 Traceback (most recent call last):
509515 ...
510516 iris.exceptions.DuplicateDataError: failed to merge into a single cube.
511517 Duplicate 'air_temperature' cube, with scalar coordinates z=Cell(point=1, bound=None)
512518
519+
513520Notice how merging the input cubes with duplicate cubes allowed produces a result
514521with **four ** `z ` coordinate values.
515522Closer inspection of these two resultant cubes demonstrates that the
@@ -578,12 +585,33 @@ To demonstrate using :func:`~iris.util.unify_time_units`,
578585let's adapt our list of input cubes from the ``concatenate_cube `` example from earlier.
579586We'll give the input cubes unequal time coordinate units and call
580587:func: `~iris.util.unify_time_units ` on the input cubes before concatenating
581- the input cubes using :meth: `~iris.cube.CubeList.concatenate_cube `::
588+ the input cubes using :meth: `~iris.cube.CubeList.concatenate_cube `:
589+
590+ .. testsetup :: concatenate_time_units
591+
592+ import numpy as np
593+ import iris
594+ def _xyt_cube(t):
595+ cube = iris.cube.Cube(np.arange(12 * len(t)).reshape(-1, 3, 4), 'air_temperature', units='kelvin')
596+ cube.add_dim_coord(iris.coords.DimCoord(range(3), long_name='y'), 1)
597+ cube.add_dim_coord(iris.coords.DimCoord(range(4), long_name='x'), 2)
598+ cube.add_dim_coord(iris.coords.DimCoord(t, long_name='t'), 0)
599+ return cube
600+ cubes = iris.cube.CubeList([_xyt_cube(np.arange(31).astype(np.float64)),
601+ _xyt_cube(np.arange(28).astype(np.float64) + 31),
602+ _xyt_cube(np.arange(31).astype(np.float64) + 59)])
603+ cubes[0].coord('t').units = 'days since 1990-02-15'
604+ cubes[1].coord('t').units = 'days since 1970-01-01'
605+ cubes[2].coord('t').units = 'days since 1970-01-01'
606+
607+ .. doctest :: concatenate_time_units
608+ :options: +ELLIPSIS, +NORMALIZE_WHITESPACE
582609
583610 >>> from iris.util import unify_time_units
584611 >>> print cubes
585- 0: air_temperature / (kelvin) (t: 10; x: 10; y: 10)
586- 1: air_temperature / (kelvin) (t: 10; x: 10; y: 10)
612+ 0: air_temperature / (kelvin) (t: 31; y: 3; x: 4)
613+ 1: air_temperature / (kelvin) (t: 28; y: 3; x: 4)
614+ 2: air_temperature / (kelvin) (t: 31; y: 3; x: 4)
587615
588616 >>> print cubes[0 ].coord(' t' ).units
589617 days since 1990-02-15
@@ -592,8 +620,8 @@ the input cubes using :meth:`~iris.cube.CubeList.concatenate_cube`::
592620
593621 >>> print cubes.concatenate_cube()
594622 Traceback (most recent call last):
595- ...
596- iris.exceptions. ConcatenateError: failed to concatenate into a single cube.
623+ ...
624+ ConcatenateError: failed to concatenate into a single cube.
597625 Dimension coordinates metadata differ: t != t
598626
599627 >>> unify_time_units(cubes)
@@ -602,12 +630,11 @@ the input cubes using :meth:`~iris.cube.CubeList.concatenate_cube`::
602630 days since 1990-02-15
603631
604632 >>> print cubes.concatenate_cube()
605- air_temperature / (kelvin) (t: 20; x: 10; y: 10 )
633+ air_temperature / (kelvin) (t: 90; y: 3; x: 4 )
606634 Dimension coordinates:
607- t x - -
608- x - x -
609- y - - x
610-
635+ t x - -
636+ y - x -
637+ x - - x
611638
612639**Attributes Mismatch **
613640
0 commit comments