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 |
|---|---|
|
Physical quantity: variable name, units, CF names |
|
Temporal coordinate: base datetime, forecast step, valid datetime |
|
Vertical coordinate: level value, level type |
|
Horizontal grid: lat/lon arrays, bounding box, projection |
|
Ensemble member identifier |
|
Post-processing operations (e.g. accumulation). Experimental. |
|
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]:
| path | /var/folders/93/w0p869rx17q98wxk83gn9ys40000gn/T/tmpmuowbdbx/url-564b8ed1a75766455b0adde3c0014e39c47c49d128d82e4af7cf03ad2fa876a2.grib |
| size | 1 KiB |
| types | fieldlist, pandas, xarray, numpy, array |
[3]:
f = ds.to_fieldlist()[0]
f
[3]:
| 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) |
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