Metadata

This section provides a detailed overview of how your metadata is managed within Iris. In particular, it discusses what metadata is, where it fits into Iris, and more importantly how you can create, access, manipulate, and analyse your metadata.

All the finer details covered here may not be entirely relevant to your use case, but it’s here if you ever need it. In fact, you may want to skip straight ahead to Richer Metadata Behaviour, and take it from there.

Introduction

As discussed in Iris Data Structures, Iris draws heavily from the NetCDF CF Metadata Conventions as a source for its data model, thus building on the widely recognised and understood terminology defined within those CF Conventions by the scientific community.

In Iris Data Structures we introduced several fundamental classes in Iris that care about your data, and also your metadata i.e., data about data. These are the Cube, the AuxCoord, and the DimCoord, all of which should be familiar to you now. In addition to these, Iris models several other classes of CF Conventions metadata. Namely,

Collectively, the aforementioned classes will be known here as the Iris CF Conventions classes.

Hint

If there are any CF Conventions metadata missing from Iris that you care about, then please let us know by raising a GitHub Issue on SciTools/iris

Common Metadata

Each of the Iris CF Conventions classes use metadata to define them and give them meaning.

The metadata used to define an Iris CF Conventions class is composed of individual metadata members, almost all of which reference specific CF Conventions terms. The individual metadata members used to define each of the Iris CF Conventions classes are shown in Table 1.

As Table 1 highlights, specific metadata is used to define and represent each Iris CF Conventions class. This means that metadata alone, can be used to easily identify, compare and differentiate between individual class instances.

For example, the collective metadata used to define an AncillaryVariable are the standard_name, long_name, var_name, units, and attributes members. Note that, these are the actual data attribute names of the metadata members on the Iris class.

Table 1 - Iris classes that model CF Conventions metadata

Metadata Members

AncillaryVariable

AuxCoord

AuxCoordFactory

CellMeasure

Cube

DimCoord

Metadata Members

standard_name

standard_name

long_name

long_name

var_name

var_name

units

units

attributes

attributes

coord_system

coord_system

climatological

climatological

measure

measure

cell_methods

cell_methods

circular

circular

Note

The var_name and circular metadata members are Iris specific terms, rather than recognised CF Conventions terms.

Common Metadata API

As of Iris 3.0.0, a unified treatment of metadata has been applied across each Iris class (Table 1) to allow users to easily manage and manipulate their metadata in a consistent way.

This is achieved through the metadata property, which allows you to manipulate the associated underlying metadata members as a collective. For example, given the following Cube,

>>> print(cube)
air_temperature / (K)               (time: 240; latitude: 37; longitude: 49)
     Dimension coordinates:
          time                           x              -              -
          latitude                       -              x              -
          longitude                      -              -              x
     Auxiliary coordinates:
          forecast_period                x              -              -
     Scalar coordinates:
          forecast_reference_time: 1859-09-01 06:00:00
          height: 1.5 m
     Attributes:
          Conventions: CF-1.5
          Model scenario: A1B
          STASH: m01s03i236
          source: Data from Met Office Unified Model 6.05
     Cell methods:
          mean: time (6 hour)

We can easily get all of the associated metadata of the Cube using the metadata property:

>>> cube.metadata
CubeMetadata(standard_name='air_temperature', long_name=None, var_name='air_temperature', units=Unit('K'), attributes={'Conventions': 'CF-1.5', 'STASH': STASH(model=1, section=3, item=236), 'Model scenario': 'A1B', 'source': 'Data from Met Office Unified Model 6.05'}, cell_methods=(CellMethod(method='mean', coord_names=('time',), intervals=('6 hour',), comments=()),))

We can also inspect the metadata of the longitude DimCoord attached to the Cube in the same way:

>>> cube.coord("longitude").metadata
DimCoordMetadata(standard_name='longitude', long_name=None, var_name='longitude', units=Unit('degrees'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)

Or use the metadata property again, but this time on the forecast_period AuxCoord attached to the Cube:

>>> cube.coord("forecast_period").metadata
CoordMetadata(standard_name='forecast_period', long_name=None, var_name='forecast_period', units=Unit('hours'), attributes={}, coord_system=None, climatological=False)

Note that, the metadata property is available on each of the Iris CF Conventions class containers referenced in Table 1, and thus provides a common and consistent approach to managing your metadata, which we’ll now explore a little more fully.

Metadata Classes

The metadata property will return an appropriate namedtuple metadata class for each Iris CF Conventions class container. The metadata class returned by each container class is shown in Table 2 below,

Table 2 - Iris namedtuple metadata classes

Container Class

Metadata Class

AncillaryVariable

AncillaryVariableMetadata

AuxCoord

CoordMetadata

AuxCoordFactory

CoordMetadata

CellMeasure

CellMeasureMetadata

Cube

CubeMetadata

DimCoord

DimCoordMetadata

Akin to the behaviour of a namedtuple, the metadata classes in Table 2 create tuple-like instances i.e., they provide a snapshot of the associated metadata member values, which are not settable, but they may be mutable depending on the data-type of the member. For example, given the following metadata of a DimCoord,

>>> longitude = cube.coord("longitude")
>>> metadata = longitude.metadata
>>> metadata
DimCoordMetadata(standard_name='longitude', long_name=None, var_name='longitude', units=Unit('degrees'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)

The metadata member value is the same as the container class member value,

>>> metadata.attributes is longitude.attributes
True
>>> metadata.circular is longitude.circular
True

Like a namedtuple, the metadata member is not settable,

>>> metadata.attributes = {"grinning face": "🙂"}
Traceback (most recent call last):
AttributeError: can't set attribute

However, for a dict member, it is mutable,

>>> metadata.attributes
{}
>>> longitude.attributes["grinning face"] = "🙂"
>>> metadata.attributes
{'grinning face': '🙂'}
>>> metadata.attributes["grinning face"] = "🙃"
>>> longitude.attributes
{'grinning face': '🙃'}

But metadata members with simple values are not mutable,

>>> metadata.circular
False
>>> longitude.circular = True
>>> metadata.circular
False

And of course, they’re also not settable,

>>> metadata.circular = True
Traceback (most recent call last):
AttributeError: can't set attribute

Note that, the metadata property re-creates a new instance per invocation, with a snapshot of the container class metadata values at that point in time,

>>> longitude.metadata
DimCoordMetadata(standard_name='longitude', long_name=None, var_name='longitude', units=Unit('degrees'), attributes={'grinning face': '🙃'}, coord_system=GeogCS(6371229.0), climatological=False, circular=True)

Skip ahead to metadata assignment for a fuller discussion on options how to set and get metadata on the instance of an Iris CF Conventions container class (Table 2).

Metadata Class Behaviour

As mentioned previously, the metadata classes in Table 2 inherit the behaviour of a namedtuple, and so act and feel like a namedtuple, just as you might expect. For example, given the following metadata,

>>> metadata
DimCoordMetadata(standard_name='longitude', long_name=None, var_name='longitude', units=Unit('degrees'), attributes={'grinning face': '🙃'}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)

We can use the namedtuple._make method to create a new DimCoordMetadata instance from an existing sequence or iterable. The number and order of the values used in the iterable must match that of the associated namedtuple._fields, which is discussed later,

>>> values = (1, 2, 3, 4, 5, 6, 7, 8)
>>> metadata._make(values)
DimCoordMetadata(standard_name=1, long_name=2, var_name=3, units=4, attributes=5, coord_system=6, climatological=7, circular=8)

Note that, namedtuple._make is a class method, and so it is possible to create a new instance directly from the metadata class itself,

>>> from iris.common import DimCoordMetadata
>>> DimCoordMetadata._make(values)
DimCoordMetadata(standard_name=1, long_name=2, var_name=3, units=4, attributes=5, coord_system=6, climatological=7, circular=8)

It is also possible to easily convert metadata to an dict using the namedtuple._asdict method. This can be particularly handy when a standard Python built-in container is required to represent your metadata,

>>> metadata._asdict()
{'standard_name': 'longitude', 'long_name': None, 'var_name': 'longitude', 'units': Unit('degrees'), 'attributes': {'grinning face': '🙃'}, 'coord_system': GeogCS(6371229.0), 'climatological': False, 'circular': False}

Using the namedtuple._replace method allows you to create a new metadata class instance, but replacing specified members with new associated values,

>>> metadata
DimCoordMetadata(standard_name='longitude', long_name=None, var_name='longitude', units=Unit('degrees'), attributes={'grinning face': '🙃'}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)
>>> metadata._replace(standard_name=None, units=None)
DimCoordMetadata(standard_name=None, long_name=None, var_name='longitude', units=None, attributes={'grinning face': '🙃'}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)

Another very useful method from the namedtuple toolkit is namedtuple._fields. This method returns a tuple of strings listing the metadata members, in a fixed order. This allows you to easily iterate over the metadata class members, for what ever purpose you may require, e.g.,

>>> metadata._fields
('standard_name', 'long_name', 'var_name', 'units', 'attributes', 'coord_system', 'climatological', 'circular')
>>> tuple([getattr(metadata, member) for member in metadata._fields])
('longitude', None, 'longitude', Unit('degrees'), {'grinning face': '🙃'}, GeogCS(6371229.0), False, False)
>>> tuple([getattr(metadata, member) for member in metadata._fields if member.endswith("name")])
('longitude', None, 'longitude')

Note that, namedtuple._fields is also a class method, so you don’t need an instance to determine the members of a metadata class, e.g.,

>>> from iris.common import CubeMetadata
>>> CubeMetadata._fields
('standard_name', 'long_name', 'var_name', 'units', 'attributes', 'cell_methods')

Aside from the benefit of metadata classes inheriting behaviour and state from namedtuple, further additional rich behaviour is also available, which we explore next.

Richer Metadata Behaviour

The metadata classes from Table 2 support additional behaviour above and beyond that of the standard Python namedtuple, which allows you to easily compare, combine, convert and understand the difference between your metadata instances.

Metadata Equality

The metadata classes support both equality (__eq__) and inequality (__ne__), but no other rich comparison operators are implemented. This is simply because there is no obvious ordering to any collective of metadata members, as defined in Table 1.

For example, given the following DimCoord,

>>> longitude.metadata
DimCoordMetadata(standard_name='longitude', long_name=None, var_name='longitude', units=Unit('degrees'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)

We can compare metadata using the == operator, as you may naturally expect,

>>> longitude.metadata == longitude.metadata
True

Or alternatively, using the equal method instead,

>>> longitude.metadata.equal(longitude.metadata)
True

Note that, the == operator (__eq__) and the equal method are both functionally equivalent. However, the equal method also provides a means to enable lenient equality, as discussed in Lenient Equality.

Strict Equality

By default, metadata class equality will perform a strict comparison between each associated metadata member. If any metadata member has a different value, then the result of the operation will be False. For example,

>>> other = longitude.metadata._replace(standard_name=None)
>>> other
DimCoordMetadata(standard_name=None, long_name=None, var_name='longitude', units=Unit('degrees'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)
>>> longitude.metadata == other
False
>>> longitude.attributes = {"grinning face": "🙂"}
>>> other = longitude.metadata._replace(attributes={"grinning face":  "🙃"})
>>> other
DimCoordMetadata(standard_name='longitude', long_name=None, var_name='longitude', units=Unit('degrees'), attributes={'grinning face': '🙃'}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)
>>> longitude.metadata == other
False

One further point worth highlighting is it is possible for NumPy scalars and arrays to appear in the attributes dict of some Iris metadata class instances. Normally, this would cause issues. For example,

>>> simply = {"one": np.int(1), "two": np.array([1.0, 2.0])}
>>> simply
{'one': 1, 'two': array([1., 2.])}
>>> fruity = {"one": np.int(1), "two": np.array([1.0, 2.0])}
>>> fruity
{'one': 1, 'two': array([1., 2.])}
>>> simply == fruity
Traceback (most recent call last):
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

However, metadata class equality is rich enough to handle this eventuality,

>>> metadata1 = cube.metadata._replace(attributes=simply)
>>> metadata2 = cube.metadata._replace(attributes=fruity)
>>> metadata1
CubeMetadata(standard_name='air_temperature', long_name=None, var_name='air_temperature', units=Unit('K'), attributes={'one': 1, 'two': array([1., 2.])}, cell_methods=(CellMethod(method='mean', coord_names=('time',), intervals=('6 hour',), comments=()),))
>>> metadata2
CubeMetadata(standard_name='air_temperature', long_name=None, var_name='air_temperature', units=Unit('K'), attributes={'one': 1, 'two': array([1., 2.])}, cell_methods=(CellMethod(method='mean', coord_names=('time',), intervals=('6 hour',), comments=()),))
>>> metadata1 == metadata2
True
>>> metadata1
CubeMetadata(standard_name='air_temperature', long_name=None, var_name='air_temperature', units=Unit('K'), attributes={'one': 1, 'two': array([1., 2.])}, cell_methods=(CellMethod(method='mean', coord_names=('time',), intervals=('6 hour',), comments=()),))
>>> metadata2 = cube.metadata._replace(attributes={"one": np.int(1), "two": np.array([1000.0, 2000.0])})
>>> metadata2
CubeMetadata(standard_name='air_temperature', long_name=None, var_name='air_temperature', units=Unit('K'), attributes={'one': 1, 'two': array([1000., 2000.])}, cell_methods=(CellMethod(method='mean', coord_names=('time',), intervals=('6 hour',), comments=()),))
>>> metadata1 == metadata2
False
Comparing Like With Like

So far in our journey through metadata class equality, we have only considered cases where the operands are instances of the same type. It is possible to compare instances of different metadata classes, but the result will always be False,

>>> cube.metadata == longitude.metadata
False

The reason different metadata classes cannot be compared is simply because each metadata class contains different members, as shown in Table 1. However, there is an exception to the rule…

Exception to the Rule

In general, different metadata classes cannot be compared, however support is provided for comparing CoordMetadata and DimCoordMetadata metadata classes. For example, consider the following DimCoordMetadata,

>>> latitude = cube.coord("latitude")
>>> latitude.metadata
DimCoordMetadata(standard_name='latitude', long_name=None, var_name='latitude', units=Unit('degrees'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)

Next we create a new CoordMetadata instance from the DimCoordMetadata instance,

>>> kwargs = latitude.metadata._asdict()
>>> del kwargs["circular"]
>>> metadata = CoordMetadata(**kwargs)
>>> metadata
CoordMetadata(standard_name='latitude', long_name=None, var_name='latitude', units=Unit('degrees'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False)

Hint

Alternatively, use the from_metadata class method instead, see Metadata Conversion.

Comparing the instances confirms that equality is indeed supported between DimCoordMetadata and CoordMetadata classes,

>>> latitude.metadata == metadata
True

The reason for this behaviour is primarily historical. The circular member has never been used by the __eq__ operator when comparing an AuxCoord and a DimCoord. Therefore for consistency, this behaviour is also extended to __eq__ for the associated container metadata classes.

However, note that the circular member is used by the __eq__ operator when comparing one DimCoord to another. This also applies when comparing DimCoordMetadata.

This exception to the rule for equality also applies to the difference and combine methods of metadata classes.

Metadata Difference

Being able to compare metadata is valuable, especially when we have the convenience of being able to do this easily with metadata classes. However, when the result of comparing two metadata instances is False, it begs the question, “what’s the difference?

Well, this is where we pull the difference method out of the metadata toolbox. First, let’s create some metadata to compare,

>>> longitude = cube.coord("longitude")
>>> longitude.metadata
DimCoordMetadata(standard_name='longitude', long_name=None, var_name='longitude', units=Unit('degrees'), attributes={'grinning face': '🙂'}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)

Now, we replace some members of the DimCoordMetadata with different values,

>>> from cf_units import Unit
>>> metadata = longitude.metadata._replace(long_name="lon", var_name="lon", units=Unit("radians"))
>>> metadata
DimCoordMetadata(standard_name='longitude', long_name='lon', var_name='lon', units=Unit('radians'), attributes={'grinning face': '🙂'}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)

First, confirm that the metadata is different,

>>> longitude.metadata != metadata
True

As expected, the metadata is different. Now, let’s answer the question, “what’s the difference?”,

>>> longitude.metadata.difference(metadata)
DimCoordMetadata(standard_name=None, long_name=(None, 'lon'), var_name=('longitude', 'lon'), units=(Unit('degrees'), Unit('radians')), attributes=None, coord_system=None, climatological=None, circular=None)

The difference method returns a DimCoordMetadata instance, when there is at least one metadata member with a different value, where,

  • None means that there was no difference for the member,

  • a tuple contains the two different associated values for the member

Given our example, only the long_name, var_name and units members have different values, as expected. Note that, the difference method is not commutative. The order of the tuple member values is the same order of the metadata class instances being compared, e.g., changing the difference instance order is reflected in the result,

>>> metadata.difference(longitude.metadata)
DimCoordMetadata(standard_name=None, long_name=('lon', None), var_name=('lon', 'longitude'), units=(Unit('radians'), Unit('degrees')), attributes=None, coord_system=None, climatological=None, circular=None)

Also, when the metadata being compared is identical, then None is simply returned,

>>> metadata.difference(metadata) is None
True

It’s worth highlighting that for the attributes dict member, only those keys with different values or missing keys will be returned by the difference method. For example, let’s customise the attributes member of the following DimCoordMetadata,

>>> attributes = {"grinning face": "😀", "neutral face": "😐"}
>>> longitude.attributes = attributes
>>> longitude.metadata
DimCoordMetadata(standard_name='longitude', long_name=None, var_name='longitude', units=Unit('degrees'), attributes={'grinning face': '😀', 'neutral face': '😐'}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)

Then create another DimCoordMetadata with a different attributes dict, namely,

  • the grinning face key has the same value,

  • the neutral face key has a different value,

  • the upside-down face key is new

>>> attributes = {"grinning face": "😀", "neutral face": "😜", "upside-down face": "🙃"}
>>> metadata = longitude.metadata._replace(attributes=attributes)
>>> metadata
DimCoordMetadata(standard_name='longitude', long_name=None, var_name='longitude', units=Unit('degrees'), attributes={'grinning face': '😀', 'neutral face': '😜', 'upside-down face': '🙃'}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)

Now, let’s compare the two above instances and see what attributes member differences we get,

>>> longitude.metadata.difference(metadata)  
DimCoordMetadata(standard_name=None, long_name=None, var_name=None, units=None, attributes=({'neutral face': '😐'}, {'neutral face': '😜', 'upside-down face': '🙃'}), coord_system=None, climatological=None, circular=None)
Diffing Like With Like

As discussed in Comparing Like With Like, it only makes sense to determine the difference between similar metadata class instances. However, note that the exception to the rule still applies here i.e., support is provided between CoordMetadata and DimCoordMetadata metadata classes.

For example, given the following AuxCoord and DimCoord,

>>> forecast_period = cube.coord("forecast_period")
>>> latitude = cube.coord("latitude")

We can inspect their associated metadata,

>>> forecast_period.metadata
CoordMetadata(standard_name='forecast_period', long_name=None, var_name='forecast_period', units=Unit('hours'), attributes={}, coord_system=None, climatological=False)
>>> latitude.metadata
DimCoordMetadata(standard_name='latitude', long_name=None, var_name='latitude', units=Unit('degrees'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)

Before comparing them to determine the values of metadata members that are different,

>>> forecast_period.metadata.difference(latitude.metadata)
CoordMetadata(standard_name=('forecast_period', 'latitude'), long_name=None, var_name=('forecast_period', 'latitude'), units=(Unit('hours'), Unit('degrees')), attributes=None, coord_system=(None, GeogCS(6371229.0)), climatological=None)
>>> latitude.metadata.difference(forecast_period.metadata)
DimCoordMetadata(standard_name=('latitude', 'forecast_period'), long_name=None, var_name=('latitude', 'forecast_period'), units=(Unit('degrees'), Unit('hours')), attributes=None, coord_system=(GeogCS(6371229.0), None), climatological=None, circular=(False, None))

In general, however, comparing different metadata classes will result in a TypeError being raised,

>>> cube.metadata.difference(longitude.metadata)
Traceback (most recent call last):
TypeError: Cannot differ 'CubeMetadata' with <class 'iris.common.metadata.DimCoordMetadata'>.

Metadata Combination

So far we’ve seen how to compare metadata, and also how to determine the difference between metadata. Now we take the next step, and explore how to combine metadata together using the combine metadata class method.

For example, consider the following CubeMetadata,

>>> cube.metadata  
CubeMetadata(standard_name='air_temperature', long_name=None, var_name='air_temperature', units=Unit('K'), attributes={'Conventions': 'CF-1.5', 'STASH': STASH(model=1, section=3, item=236), 'Model scenario': 'A1B', 'source': 'Data from Met Office Unified Model 6.05'}, cell_methods=(CellMethod(method='mean', coord_names=('time',), intervals=('6 hour',), comments=()),))

We can perform the identity function by comparing the metadata with itself,

>>> metadata = cube.metadata.combine(cube.metadata)
>>> cube.metadata == metadata
True

As you might expect, combining identical metadata returns metadata that is also identical.

The combine method will always return a new metadata class instance, where each metadata member is either None or populated with a common value. Let’s clarify this, by combining our above CubeMetadata with another instance that’s identical apart from its standard_name member, which is replaced with a different value,

>>> metadata = cube.metadata._replace(standard_name="air_pressure_at_sea_level")
>>> metadata != cube.metadata
True
>>> metadata.combine(cube.metadata)  
CubeMetadata(standard_name=None, long_name=None, var_name='air_temperature', units=Unit('K'), attributes={'STASH': STASH(model=1, section=3, item=236), 'source': 'Data from Met Office Unified Model 6.05', 'Model scenario': 'A1B', 'Conventions': 'CF-1.5'}, cell_methods=(CellMethod(method='mean', coord_names=('time',), intervals=('6 hour',), comments=()),))

The combine method combines metadata by performing a strict comparison between each of the associated metadata member values,

  • if the values are different, then the combined result is None

  • otherwise, the combined result is the common value

Let’s reinforce this behaviour, but this time by combining metadata where the attributes dict member is different, where,

  • the STASH and source keys are missing,

  • the Model scenario key has the same value,

  • the Conventions key has a different value,

  • the grinning face key is new

>>> attributes = {"Model scenario": "A1B", "Conventions": "CF-1.8", "grinning face": "🙂" }
>>> metadata = cube.metadata._replace(attributes=attributes)
>>> metadata != cube.metadata
True
>>> metadata.combine(cube.metadata).attributes
{'Model scenario': 'A1B'}

The combined result for the attributes member only contains those common keys with common values.

Note that, the combine method is commutative,

>>> cube.metadata.combine(metadata) == metadata.combine(cube.metadata)
True

Although, this is only the case when combining instances of the same metadata class. This is explored in a little further detail next.

Combine Like With Like

Akin to the equal and difference methods, only instances of similar metadata classes can be combined, otherwise a TypeError is raised,

>>> cube.metadata.combine(longitude.metadata)
Traceback (most recent call last):
TypeError: Cannot combine 'CubeMetadata' with <class 'iris.common.metadata.DimCoordMetadata'>.

Again, however, the exception to the rule also applies here i.e., support is provided between CoordMetadata and DimCoordMetadata metadata classes.

For example, we can combine the metadata of the following AuxCoord and DimCoord,

>>> forecast_period = cube.coord("forecast_period")
>>> longitude = cube.coord("longitude")

First, let’s see their associated metadata,

>>> forecast_period.metadata
CoordMetadata(standard_name='forecast_period', long_name=None, var_name='forecast_period', units=Unit('hours'), attributes={}, coord_system=None, climatological=False)
>>> longitude.metadata
DimCoordMetadata(standard_name='longitude', long_name=None, var_name='longitude', units=Unit('degrees'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)

Before combining their metadata together,

>>> forecast_period.metadata.combine(longitude.metadata)
CoordMetadata(standard_name=None, long_name=None, var_name=None, units=None, attributes={}, coord_system=None, climatological=False)
>>> longitude.metadata.combine(forecast_period.metadata)
DimCoordMetadata(standard_name=None, long_name=None, var_name=None, units=None, attributes={}, coord_system=None, climatological=False, circular=None)

However, note that commutativity in this case cannot be honoured, for obvious reasons.

Metadata Conversion

In general, the equal, difference, and combine methods only support operations on instances of the same metadata class (see exception to the rule).

However, metadata may be converted from one metadata class to another using the from_metadata class method. For example, given the following CubeMetadata,

>>> cube.metadata  
CubeMetadata(standard_name='air_temperature', long_name=None, var_name='air_temperature', units=Unit('K'), attributes={'Conventions': 'CF-1.5', 'STASH': STASH(model=1, section=3, item=236), 'Model scenario': 'A1B', 'source': 'Data from Met Office Unified Model 6.05'}, cell_methods=(CellMethod(method='mean', coord_names=('time',), intervals=('6 hour',), comments=()),))

We can easily convert it to a DimCoordMetadata instance using from_metadata,

>>> DimCoordMetadata.from_metadata(cube.metadata)  
DimCoordMetadata(standard_name='air_temperature', long_name=None, var_name='air_temperature', units=Unit('K'), attributes={'Conventions': 'CF-1.5', 'STASH': STASH(model=1, section=3, item=236), 'Model scenario': 'A1B', 'source': 'Data from Met Office Unified Model 6.05'}, coord_system=None, climatological=None, circular=None)

By examining Table 1, we can see that the Cube and DimCoord container classes share the following common metadata members,

  • standard_name,

  • long_name,

  • var_name,

  • units,

  • attributes

As such, all of these metadata members of the resultant DimCoordMetadata instance are populated from the associated CubeMetadata instance members. However, a CubeMetadata class does not contain the following DimCoordMetadata members,

  • coords_system,

  • climatological,

  • circular

Thus these particular metadata members are set to None in the resultant DimCoordMetadata instance.

Note that, the from_metadata method is also available on a metadata class instance,

>>> longitude.metadata.from_metadata(cube.metadata)
DimCoordMetadata(standard_name='air_temperature', long_name=None, var_name='air_temperature', units=Unit('K'), attributes={'Conventions': 'CF-1.5', 'STASH': STASH(model=1, section=3, item=236), 'Model scenario': 'A1B', 'source': 'Data from Met Office Unified Model 6.05'}, coord_system=None, climatological=None, circular=None)

Metadata Assignment

The metadata property available on each Iris CF Conventions container class (Table 2) can not only be used to get the metadata of an instance, but also to set the metadata on an instance.

For example, given the following DimCoordMetadata of the longitude coordinate,

>>> longitude.metadata
DimCoordMetadata(standard_name='longitude', long_name=None, var_name='longitude', units=Unit('degrees'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)

We can assign to it directly using the DimCoordMetadata of the latitude coordinate,

>>> latitude.metadata
DimCoordMetadata(standard_name='latitude', long_name=None, var_name='latitude', units=Unit('degrees'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)
>>> longitude.metadata = latitude.metadata
>>> longitude.metadata
DimCoordMetadata(standard_name='latitude', long_name=None, var_name='latitude', units=Unit('degrees'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)
Assign by Iterable

It is also possible to assign to the metadata property of an Iris CF Conventions container with an iterable containing the correct number of associated member values, e.g.,

>>> values = [getattr(latitude, member) for member in latitude.metadata._fields]
>>> longitude.metadata = values
>>> longitude.metadata
DimCoordMetadata(standard_name='latitude', long_name=None, var_name='latitude', units=Unit('degrees'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)
Assign by Namedtuple

A namedtuple may also be used to assign to the metadata property of an Iris CF Conventions container. For example, let’s first create a custom namedtuple class,

>>> from collections import namedtuple
>>> Metadata = namedtuple("Metadata", ["standard_name", "long_name", "var_name", "units", "attributes", "coord_system", "climatological", "circular"])

Now create an instance of this custom namedtuple class, and populate it,

>>> metadata = Metadata(*values)
>>> metadata
Metadata(standard_name='latitude', long_name=None, var_name='latitude', units=Unit('degrees'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)

Now we can use the custom namedtuple instance to assign directly to the metadata of the longitude coordinate,

>>> longitude.metadata = metadata
>>> longitude.metadata
DimCoordMetadata(standard_name='latitude', long_name=None, var_name='latitude', units=Unit('degrees'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)
Assign by Mapping

It is also possible to assign to the metadata property using a mapping, such as a dict,

>>> mapping = latitude.metadata._asdict()
>>> mapping
{'standard_name': 'latitude', 'long_name': None, 'var_name': 'latitude', 'units': Unit('degrees'), 'attributes': {}, 'coord_system': GeogCS(6371229.0), 'climatological': False, 'circular': False}
>>> longitude.metadata = mapping
>>> longitude.metadata
DimCoordMetadata(standard_name='latitude', long_name=None, var_name='latitude', units=Unit('degrees'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)

Support is also provided for assigning a partial mapping, for example,

>>> longitude.metadata
DimCoordMetadata(standard_name='longitude', long_name=None, var_name='longitude', units=Unit('degrees'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)
>>> longitude.metadata = dict(var_name="lat", units="radians", circular=True)
>>> longitude.metadata
DimCoordMetadata(standard_name='longitude', long_name=None, var_name='lat', units=Unit('radians'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False, circular=True)

Indeed, it’s also possible to assign to the metadata property with a different metadata class instance,

>>> longitude.metadata
DimCoordMetadata(standard_name='longitude', long_name=None, var_name='longitude', units=Unit('degrees'), attributes={}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)
>>> longitude.metadata = cube.metadata
>>> longitude.metadata  
DimCoordMetadata(standard_name='air_temperature', long_name=None, var_name='air_temperature', units=Unit('K'), attributes={'Conventions': 'CF-1.5', 'STASH': STASH(model=1, section=3, item=236), 'Model scenario': 'A1B', 'source': 'Data from Met Office Unified Model 6.05'}, coord_system=GeogCS(6371229.0), climatological=False, circular=False)

Note that, only common metadata members will be assigned new associated values. All other metadata members will be left unaltered.