Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion src/eva/data/ioda_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import netCDF4 as nc
import numpy as np
import pandas as pd

# --------------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -69,6 +70,35 @@ def subset_channels(ds, channels):

return ds

# --------------------------------------------------------------------------------------------------
# Valid time helper


def _validtime_iso_to_datetime64ns(values, units=None):
"""
Convert ISO-8601 strings (or byte-strings) such as "YYYY-MM-DDTHH:MM:SSZ"
to numpy datetime64[ns] in UTC and tz-naive form (friendly for Matplotlib).

Parameters
----------
values : array-like
String/bytes/object array of ISO-8601 timestamps.

Returns
-------
numpy.ndarray
Array of dtype datetime64[ns] with original shape.
"""
a = np.asarray(values)
# bytes -> unicode first
if a.dtype.kind == "S":
a = a.astype("U")
# only convert strings/objects; otherwise return as-is
if a.dtype.kind in {"U", "O"}:
ts = pd.to_datetime(a.ravel(), utc=True, errors="coerce").tz_convert(None)
return ts.to_numpy(dtype="datetime64[ns]").reshape(a.shape)
return values


# --------------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -186,6 +216,15 @@ def execute(self, dataset_config, data_collections, timing):
domain = ds_header['statisticDomain']
add_domain = True

# Normalize validTime from ISO8601 strings to datetime64[ns]
if add_validTime:
vt_vals = _validtime_iso_to_datetime64ns(validTime.values)
validTime = DataArray(
vt_vals,
dims=validTime.dims,
attrs={**getattr(validTime, "attrs", {}), "axis_type": "time"}
)

ds_header.close()

# Set the channels based on user selection and add channels variable
Expand Down Expand Up @@ -251,7 +290,9 @@ def execute(self, dataset_config, data_collections, timing):
ds['MetaData::channelNumber'] = sensor_channels

if add_validTime:
ds['analysisCycle'] = validTime
# Expose normalized time as a coordinate for concat,
# and as a data variable for plotting/backends.
ds = ds.assign_coords(analysisCycle=validTime)
ds['MetaData::validTime'] = validTime

# Set channels
Expand Down