Source code for GINCCO_lib.modules.temporal_mean
import numpy as np
from datetime import datetime
def _to_np_day(d: datetime) -> np.datetime64:
"""Convert Python datetime to numpy datetime64 at day resolution."""
return np.datetime64(d.date(), 'D')
[docs]
def monthly_mean(data: np.ndarray, tstart: datetime, tend: datetime, time_axis: int = 0):
"""
Compute monthly means for daily, contiguous data in [tstart, tend] (inclusive).
Parameters
----------
data : np.ndarray
Input array. One axis is time (daily).
tstart, tend : datetime.datetime
Inclusive range of the data.
time_axis : int
Axis that represents time in `data`.
Returns
-------
monthly : np.ndarray
Monthly means with time axis replaced by number of months.
month_labels : np.ndarray of datetime64[M]
Month labels for each output slice.
"""
# Normalize time axis to front
x = np.moveaxis(data, time_axis, 0)
start_d = _to_np_day(tstart)
end_d = _to_np_day(tend)
# Expected number of days (inclusive)
n_days = int((end_d - start_d) / np.timedelta64(1, 'D')) + 1
if x.shape[0] != n_days:
raise ValueError(f"Expected {n_days} days from {tstart.date()} to {tend.date()}, got {x.shape[0]}.")
# Daily timestamps
time_vec = start_d + np.arange(n_days).astype('timedelta64[D]')
# Month labels
y1, m1 = tstart.year, tstart.month
y2, m2 = tend.year, tend.month
n_months = (y2 - y1) * 12 + (m2 - m1) + 1
month0 = np.datetime64(f"{y1:04d}-{m1:02d}", 'M')
month_labels = month0 + np.arange(n_months).astype('timedelta64[M]')
# Map each day to its month
time_months = time_vec.astype('datetime64[M]')
# Aggregate
out = np.empty((n_months,) + x.shape[1:], dtype=float)
for i, m in enumerate(month_labels):
sel = (time_months == m)
out[i] = np.nanmean(x[sel, ...], axis=0)
# Restore original axis layout
monthly = np.moveaxis(out, 0, time_axis)
return monthly, month_labels
[docs]
def annual_mean(data: np.ndarray, tstart: datetime, tend: datetime, time_axis: int = 0):
"""
Compute annual means for daily, contiguous data in [tstart, tend] (inclusive).
Partial first or last years are averaged over the available days.
Parameters
----------
data : np.ndarray
Input array. One axis is time (daily).
tstart, tend : datetime.datetime
Inclusive range of the data.
time_axis : int
Axis that represents time in `data`.
Returns
-------
yearly : np.ndarray
Annual means with time axis replaced by number of years.
year_labels : np.ndarray of datetime64[Y]
Year labels for each output slice.
"""
x = np.moveaxis(data, time_axis, 0)
start_d = _to_np_day(tstart)
end_d = _to_np_day(tend)
n_days = int((end_d - start_d) / np.timedelta64(1, 'D')) + 1
if x.shape[0] != n_days:
raise ValueError(f"Expected {n_days} days from {tstart.date()} to {tend.date()}, got {x.shape[0]}.")
time_vec = start_d + np.arange(n_days).astype('timedelta64[D]')
y1, y2 = tstart.year, tend.year
n_years = (y2 - y1) + 1
year0 = np.datetime64(f"{y1:04d}", 'Y')
year_labels = year0 + np.arange(n_years).astype('timedelta64[Y]')
time_years = time_vec.astype('datetime64[Y]')
out = np.empty((n_years,) + x.shape[1:], dtype=float)
for i, y in enumerate(year_labels):
sel = (time_years == y)
out[i] = np.nanmean(x[sel, ...], axis=0)
yearly = np.moveaxis(out, 0, time_axis)
return yearly, year_labels