Field overview

A Field represents a single horizontal slice of a geophysical quantity at a particular time and vertical level. It bundles together the data values and a set of replaceable metadata components, each responsible for a distinct aspect of the metadata:

Attribute

What it describes

field.parameter

Physical quantity: variable name, units, CF names

field.time

Temporal coordinate: base datetime, forecast step, valid datetime

field.vertical

Vertical coordinate: level value, level type

field.geography

Horizontal grid: lat/lon arrays, bounding box, projection

field.ensemble

Ensemble member identifier

field.proc

Post-processing operations (e.g. accumulation). Experimental.

field.labels

User-defined key-value pairs attached to the field

All component keys are also accessible through the generic get() interface. See Metadata key reference for the full list of high-level metadata keys.

Load data

[1]:
import earthkit.data as ekd
[2]:
ds = ekd.from_source("sample", "test.grib")
ds
[2]:
GRIB file

path/var/folders/93/w0p869rx17q98wxk83gn9ys40000gn/T/tmpmuowbdbx/url-564b8ed1a75766455b0adde3c0014e39c47c49d128d82e4af7cf03ad2fa876a2.grib
size1 KiB
typesfieldlist, pandas, xarray, numpy, array
[3]:
f = ds.to_fieldlist()[0]
f
[3]:
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)

Parameter component

The parameter component describes the physical quantity the field represents.

[4]:
print(f.parameter.variable())  # short variable name
print(f.parameter.units())  # native units
print(f.parameter.standard_name())  # CF standard name
print(f.parameter.long_name())  # human-readable description

# Same information via the generic get() interface
print(f.get("parameter.variable"))
print(f.get("parameter.units"))
2t
kelvin
unknown
2 metre temperature
2t
kelvin

Time component

The time component describes the temporal coordinate: base datetime, forecast step, and valid datetime.

[5]:
print(f.time.base_datetime())  # analysis/reference time
print(f.time.step())  # forecast step as timedelta
print(f.time.valid_datetime())  # base_datetime + step

# Same information via get()
print(f.get("time.base_datetime"))
print(f.get("time.step"))
2020-05-13 12:00:00
0:00:00
2020-05-13 12:00:00
2020-05-13 12:00:00
0:00:00

Vertical component

The vertical component describes where in the atmosphere, ocean, or land the field is defined.

[6]:
print(f.vertical.level())  # level value in native units
print(f.vertical.level_type())  # e.g. "surface", "pressure", "hybrid"
print(f.vertical.units())  # units of the level value
print(f.vertical.positive())  # "up" or "down"

# Same information via get()
print(f.get("vertical.level"))
print(f.get("vertical.level_type"))
0
surface
dimensionless
None
0
surface

Geography component

The geography component describes the horizontal grid: shape, bounding box, projection, and coordinates.

[7]:
print(f.geography.shape())  # grid dimensions
print(f.geography.area())  # bounding box (north, west, south, east)
print(f.geography.grid_type())  # e.g. "regular_ll", "reduced_gg"

# Retrieve all lat/lon coordinates for every grid point
lat, lon = f.geography.latlons()
print(lat.shape, lon.shape)

# Same information via get()
print(f.get("geography.shape"))
print(f.get("geography.area"))
(11, 19)
(73.0, -27.0, 33.0, 45.0)
regular_ll
(11, 19) (11, 19)
(11, 19)
(73.0, -27.0, 33.0, 45.0)

Labels component

The :ref:labels component <labels_component> holds user-defined key-value pairs. Unlike the other components, labels are not populated from the source data — they start empty and are filled by the user.

[8]:
# Labels are empty by default
print(f.labels)  # {}
{}
[9]:
# Add labels with "labels.<key>" entries in set()
f_lab = f.set({"labels.source": "reanalysis", "labels.experiment": 42})
print(f_lab.labels)  # {'source': 'reanalysis', 'experiment': 42}
print(f.labels)  # {} — original field unchanged
{'source': 'reanalysis', 'experiment': 42}
{}
[10]:
# Subsequent set() calls accumulate labels rather than replacing them
f_lab2 = f_lab.set({"labels.run": "ctrl"})
print(f_lab2.labels)  # {'source': 'reanalysis', 'experiment': 42, 'run': 'ctrl'}
{'source': 'reanalysis', 'experiment': 42, 'run': 'ctrl'}
[11]:
# Labels support the standard dict interface
print(f_lab2.labels["source"])  # 'reanalysis'
print(f_lab2.labels.get("experiment"))  # 42
print(f_lab2.labels.get("missing_key", "default"))  # 'default'
reanalysis
42
default
[12]:
# The generic get() interface also understands "labels.<key>",
# so labels can be used in sel(), order_by(), and metadata()
print(f_lab2.get("labels.source"))  # 'reanalysis'
print(f_lab2.get("labels.experiment"))  # 42
reanalysis
42

For a full description of the labels component see Labels component and also the :ref:/tutorials/field/field_labels.ipynb` notebook.

Data values

The values returns a copy of the underlying flat 1-D array. Use to_numpy() to get an array shaped to the grid.

[13]:
v = f.values  # flat 1-D copy
print(v.shape, v.dtype)
print(f"min={v.min():.2f}  max={v.max():.2f}")

v2d = f.to_numpy()  # shaped to (Nj, Ni)
print(v2d.shape)
(209,) float64
min=262.78  max=315.46
(11, 19)

Modifying a field

All components are immutable. Use set() to derive a modified copy. The original field is never changed.

[14]:
# Change the forecast step
new_f = f.set({"time.step": 6})
print(f.time.step(), "->", new_f.time.step())

# Change the parameter variable and units
new_f2 = f.set({"parameter.variable": "msl", "parameter.units": "Pa"})
print(f.parameter.variable(), "->", new_f2.parameter.variable())

# Change the level
new_f3 = f.set({"vertical.level": 500, "vertical.level_type": "pressure"})
print(f.vertical.level(), "->", new_f3.vertical.level())

# Replace the data values (must match the grid shape)
new_values = f.values + 10.0
new_f4 = f.set({"values": new_values})
print(f"original mean={f.values.mean():.2f}  modified mean={new_f4.values.mean():.2f}")
0:00:00 -> 6:00:00
2t -> msl
0 -> 500
original mean=283.99  modified mean=293.99