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]:
| number_of_values | 209 |
| array_type | ndarray |
| array_dtype | float64 |
| variable | 2t |
| standard_name | unknown |
| long_name | 2 metre temperature |
| units | kelvin |
| valid_datetime | 2020-05-13 12:00:00 |
| base_datetime | 2020-05-13 12:00:00 |
| step | 0:00:00 |
| level | 0 |
| layer | None |
| level_type | surface |
| member | 0 |
| grid_spec | {'area': [73, -27, 33, 45], 'grid': [4, 4], 'reference': [1, 1]} |
| grid_type | regular_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'}
[ ]: