GRIB: modifying fields
This notebook explains how to use clone() and copy() to create new fields. The main difference between these methods can be summarised as follows:
clone(): the new field keeps a reference to the original field and allows more flexible metadata modification than usingcopy()copy(): always creates a deep copy. The resulting field is always anArrayFieldstoring the all the data in memory
We will use the first field of the input data in the examples.
[1]:
import earthkit.data as ekd
ds = ekd.from_source("file", "test4.grib")
f_ori = ds[0]
f_ori.ls()
[1]:
| centre | shortName | typeOfLevel | level | dataDate | dataTime | stepRange | dataType | number | gridType | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | ecmf | t | isobaricInhPa | 500 | 20070101 | 1200 | 0 | an | 0 | regular_ll |
Using clone()
Modifying metadata
[2]:
f_new = f_ori.clone(level=700)
f_new.ls()
[2]:
| centre | shortName | typeOfLevel | level | dataDate | dataTime | stepRange | dataType | number | gridType | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | ecmf | t | isobaricInhPa | 700 | 20070101 | 1200 | 0 | an | 0 | regular_ll |
Please note that the metadata object in the new field is a wrapper around the original one and
override() is not called to update it with the newly set keys. This means that there is no guarantee that the newly set keys are compatible with original metadata. So, arbitrary metadata keys can be set with no checks performed on their validity. This is generally not a problem for most of the field methods (e.g. sel()) but can be an issue we we try to write the modified fields into a GRIB file (see below).
To demonstrate what was said above we set “level” to an invalid value and add a custom (non GRIB) key:
[3]:
f_new = f_ori.clone(level="abc", my_key="123")
f_new.ls(extra_keys="my_key")
[3]:
| centre | shortName | typeOfLevel | level | dataDate | dataTime | stepRange | dataType | number | gridType | my_key | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | ecmf | t | isobaricInhPa | abc | 20070101 | 1200 | 0 | an | 0 | regular_ll | 123 |
Metadata values passed to clone() can be callables.
[4]:
def _f(field, key, original_metadata):
return original_metadata["param"] + str(original_metadata["level"])
f_new = f_ori.clone(custom_name=_f)
f_new.ls(extra_keys="custom_name")
[4]:
| centre | shortName | typeOfLevel | level | dataDate | dataTime | stepRange | dataType | number | gridType | custom_name | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | ecmf | t | isobaricInhPa | 500 | 20070101 | 1200 | 0 | an | 0 | regular_ll | t500 |
Modifying values
[5]:
f_new = f_ori.clone(values=f_ori.values + 1)
f_new.values[0:3], f_ori.values[0:3]
[5]:
(array([229.04600525, 229.04600525, 229.04600525]),
array([228.04600525, 228.04600525, 228.04600525]))
Modifying both metadata and values
[6]:
f_new = f_ori.clone(values=f_ori.values + 1, level=700)
f_new.ls()
[6]:
| centre | shortName | typeOfLevel | level | dataDate | dataTime | stepRange | dataType | number | gridType | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | ecmf | t | isobaricInhPa | 700 | 20070101 | 1200 | 0 | an | 0 | regular_ll |
[7]:
f_new.values[0:3]
[7]:
array([229.04600525, 229.04600525, 229.04600525])
Saving the modifield field into a GRIB file
[8]:
f_new.save("_modified_field.grib")
ds_new = ekd.from_source("file", "_modified_field.grib")
ds_new[0].ls()
/var/folders/93/w0p869rx17q98wxk83gn9ys40000gn/T/ipykernel_19855/457062347.py:1: DeprecatedWarning: save is deprecated as of 0.13.0 and will be removed in 0.14.0. Use to_target() instead
f_new.save("_modified_field.grib")
[8]:
| centre | shortName | typeOfLevel | level | dataDate | dataTime | stepRange | dataType | number | gridType | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | ecmf | t | isobaricInhPa | 700 | 20070101 | 1200 | 0 | an | 0 | regular_ll |
[9]:
ds_new[0].values[0:3]
[9]:
array([229.04600525, 229.04600525, 229.04600525])
Saving is not always possible
Saving the modified field into a GRIB is only possible if the modified metadata is GRIB compatible. To demonstrate this we add a custom metadata key called “_level”.
[10]:
f_new = f_ori.clone(_level=700)
f_new.metadata("_level", "level")
[10]:
(700, 500)
Writing to GRIB is not possible because “_level” is not a valid GRIB key and ecCodes raises an exception.
[11]:
try:
f_new.to_target("file", "_modified_field1.grib")
except Exception as e:
print(e)
Error setting _level=700
Key/value not found
Traceback (most recent call last):
File "/Users/cgr/git/earthkit-data/src/earthkit/data/utils/message.py", line 274, in set
return eccodes.codes_set(self._handle, name, value)
File "/Users/cgr/git/eccodes-python/gribapi/gribapi.py", line 2140, in grib_set
grib_set_long(msgid, key, value)
File "/Users/cgr/git/eccodes-python/gribapi/gribapi.py", line 1006, in grib_set_long
GRIB_CHECK(lib.grib_set_long(h, key.encode(ENC), value))
File "/Users/cgr/git/eccodes-python/gribapi/gribapi.py", line 226, in GRIB_CHECK
errors.raise_grib_error(errid)
File "/Users/cgr/git/eccodes-python/gribapi/errors.py", line 381, in raise_grib_error
raise ERROR_MAP[errid](errid)
gribapi.errors.KeyValueNotFoundError: Key/value not found
Key/value not found
Using copy()
copy() performs a deep copy to generate an ArrayField storing all the all the data in memory.
When copy() is called without arguments both the values and the Metadata object of the original field are copied into the new field. The latter copy is created by calling override() on the original metadata object.
[12]:
f_new = f_ori.copy()
f_new.values[:3], f_ori.values[:3]
[12]:
(array([228.04600525, 228.04600525, 228.04600525]),
array([228.04600525, 228.04600525, 228.04600525]))
We can pass new values to copy().
[13]:
f_new = f_ori.copy(values=f_ori.values + 1)
f_new.values[:3], f_ori.values[:3]
[13]:
(array([229.04600525, 229.04600525, 229.04600525]),
array([228.04600525, 228.04600525, 228.04600525]))
We can also pass a new metadata object.
[14]:
f_new = f_ori.copy(metadata=f_ori.metadata().override(level=300))
f_new.ls()
[14]:
| centre | shortName | typeOfLevel | level | dataDate | dataTime | stepRange | dataType | number | gridType | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | ecmf | t | isobaricInhPa | 300 | 20070101 | 1200 | 0 | an | 0 | regular_ll |
[ ]: