Field labels

Every Field carries a labels component — a lightweight, user-defined key-value store that you can use to attach arbitrary annotations to a field. Unlike the other components (parameter, time, vertical, …), labels are not populated from the source data: they start empty and are entirely under your control.

Labels are accessible through the field.labels attribute and are stored in a SimpleLabels object.

Load data

[1]:
import earthkit.data as ekd
[2]:
ds = ekd.from_source("sample", "test.grib")
f = ds.to_fieldlist()[0]
f
[2]:
Field
number_of_values209
array_typendarray
array_dtypefloat64
variable2t
standard_nameunknown
long_name2 metre temperature
unitskelvin
valid_datetime2020-05-13 12:00:00
base_datetime2020-05-13 12:00:00
step0:00:00
level0
layerNone
level_typesurface
member0
grid_spec{'area': [73, -27, 33, 45], 'grid': [4, 4], 'reference': [1, 1]}
grid_typeregular_ll
shape(11, 19)
area(73.0, -27.0, 33.0, 45.0)

Inspecting labels

By default the labels component is empty.

[3]:
print(f.labels)  # empty by default
print(len(f.labels))  # 0
{}
0

Adding labels

Use field.set() with "labels.<key>" entries to create a new field that carries the requested labels. The original field is never modified.

[4]:
f1 = f.set({"labels.source": "reanalysis", "labels.experiment": 42})
print(f1.labels)  # {'source': 'reanalysis', 'experiment': 42}
print(f.labels)  # {} — original unchanged
{'source': 'reanalysis', 'experiment': 42}
{}

Multiple set() calls accumulate label entries rather than replacing the whole labels object:

[5]:
f2 = f1.set({"labels.run": "ctrl"})
print(f2.labels)  # {'source': 'reanalysis', 'experiment': 42, 'run': 'ctrl'}
{'source': 'reanalysis', 'experiment': 42, 'run': 'ctrl'}

To update an existing label, simply set it again:

[6]:
f3 = f1.set({"labels.experiment": 99})
print(f3.labels)  # {'source': 'reanalysis', 'experiment': 99}
{'source': 'reanalysis', 'experiment': 99}

Reading labels

SimpleLabels supports the standard dictionary interface.

[7]:
print(f2.labels["source"])  # 'reanalysis'
print(f2.labels.get("experiment"))  # 42
print(f2.labels.get("missing", "default"))  # 'default'
print(f2.labels.get("experiment", astype=str))  # '42'
print("source" in f2.labels)  # True
print(len(f2.labels))  # 3
reanalysis
42
default
42
True
3
[8]:
print(list(f2.labels.keys()))
print(list(f2.labels.items()))
['source', 'experiment', 'run']
[('source', 'reanalysis'), ('experiment', 42), ('run', 'ctrl')]

The generic field.get() method also understands the "labels.<key>" prefix, so label keys integrate seamlessly with sel(), order_by(), and metadata():

[9]:
print(f2.get("labels.source"))  # 'reanalysis'
print(f2.get("labels.experiment"))  # 42
reanalysis
42

Removing labels

SimpleLabels.remove() returns a new labels object with the specified keys dropped. Pass it back to field.set() to get a new field.

[10]:
# Remove a single label
new_labels = f2.labels.remove("run")
f4 = f2.set(labels=new_labels)
print(f4.labels)  # {'source': 'reanalysis', 'experiment': 42}
{'source': 'reanalysis', 'experiment': 42}
[11]:
# Remove multiple labels at once
new_labels = f2.labels.remove("source", "run")
f5 = f2.set(labels=new_labels)
print(f5.labels)  # {'experiment': 42}
{'experiment': 42}

Using labels in sel() and order_by()

Because label keys are exposed through field.get(), you can use them in FieldList.sel() and FieldList.order_by() exactly like any other metadata key.

[12]:
# Build a small fieldlist where each field has different labels
fl = ds.to_fieldlist()

# Annotate the first two fields with a 'source' label
annotated = [
    fl[0].set({"labels.source": "era5", "labels.member": 0}),
    fl[0].set({"labels.source": "era5", "labels.member": 1}),
    fl[0].set({"labels.source": "cerra", "labels.member": 0}),
]
ann_fl = ekd.create_fieldlist(annotated)

# Select only the ERA5 fields
era5_fl = ann_fl.sel(**{"labels.source": "era5"})
print(len(era5_fl), "field(s) selected")

# Sort by member index
sorted_fl = ann_fl.order_by("labels.member")
print([f.get("labels.member") for f in sorted_fl])
2 field(s) selected
[0, 0, 1]

Labels and GRIB data

When a field originates from a GRIB message, adding labels does not alter the raw GRIB metadata. ecCodes keys remain fully accessible via the "metadata.<key>" prefix.

[13]:
f_labelled = f.set({"labels.my_label": "val"})

print(f_labelled.get("labels.my_label"))  # 'val'
print(f_labelled.get("metadata.shortName"))  # raw GRIB key still intact
print(f_labelled.get("parameter.variable"))  # high-level key still works
val
2t
2t

Converting labels to a plain dict

[14]:
d = f2.labels.to_dict()
print(type(d), d)
<class 'dict'> {'source': 'reanalysis', 'experiment': 42, 'run': 'ctrl'}
[ ]: