Port 983bceb Extract the grading day-of-year window helpers from monorepo (drift reconciliation)

Claude-Session: https://claude.ai/code/session_01RdARHDJaYC1wSQRTYM6t3Z
This commit is contained in:
Emi Griffith 2026-07-22 12:08:03 -07:00
parent b1869af70a
commit 8cec935b06

View file

@ -144,6 +144,21 @@ def window_mask(doys: np.ndarray, target_doy: int, half: int = HALF_WINDOW) -> n
return circular <= half return circular <= half
def _window(df: pl.DataFrame, target_doy: int) -> pl.DataFrame:
"""The ±7-day seasonal sub-frame around a day-of-year — the reference sample
every per-day-of-year summary (climatology, day detail, grading) draws from."""
return df.filter(window_mask(df["doy"].to_numpy(), target_doy))
def _window_meta(sub: pl.DataFrame) -> dict:
"""Sample size and the [min, max] calendar years covered by a window sub-frame."""
years = sub["date"].dt.year()
return {
"n_samples": int(len(sub)),
"year_range": [int(years.min()), int(years.max())] if len(sub) else None,
}
def empirical_percentile(samples: np.ndarray, value) -> float | None: def empirical_percentile(samples: np.ndarray, value) -> float | None:
"""Mid-rank percentile of `value` within `samples` (handles ties correctly).""" """Mid-rank percentile of `value` within `samples` (handles ties correctly)."""
n = samples.size n = samples.size
@ -156,14 +171,8 @@ def empirical_percentile(samples: np.ndarray, value) -> float | None:
def climatology(df: pl.DataFrame, target_doy: int) -> dict: def climatology(df: pl.DataFrame, target_doy: int) -> dict:
"""Summarize the +/-7 day historical distribution for one day of the year.""" """Summarize the +/-7 day historical distribution for one day of the year."""
doys = df["doy"].to_numpy() sub = _window(df, target_doy)
sub = df.filter(window_mask(doys, target_doy)) out = {"target_doy": int(target_doy), **_window_meta(sub)}
years = sub["date"].dt.year()
out = {
"target_doy": int(target_doy),
"n_samples": int(len(sub)),
"year_range": [int(years.min()), int(years.max())] if len(sub) else None,
}
for var in CLIMO_METRICS: for var in CLIMO_METRICS:
if var not in sub.columns: if var not in sub.columns:
out[var] = None out[var] = None
@ -400,8 +409,7 @@ def day_detail(df: pl.DataFrame, date, obs: dict | None) -> dict:
in the record then only the climatological ladders are returned.""" in the record then only the climatological ladders are returned."""
d = _as_date(date) d = _as_date(date)
doy = d.timetuple().tm_yday doy = d.timetuple().tm_yday
sub = df.filter(window_mask(df["doy"].to_numpy(), doy)) sub = _window(df, doy)
years = sub["date"].dt.year()
metrics = {} metrics = {}
for var in TEMP_METRICS: for var in TEMP_METRICS:
@ -420,8 +428,7 @@ def day_detail(df: pl.DataFrame, date, obs: dict | None) -> dict:
return { return {
"date": d.isoformat(), "date": d.isoformat(),
"doy": doy, "doy": doy,
"n_samples": int(len(sub)), **_window_meta(sub),
"year_range": [int(years.min()), int(years.max())] if len(sub) else None,
"metrics": metrics, "metrics": metrics,
} }
@ -430,8 +437,7 @@ def grade_day(df: pl.DataFrame, date, obs: dict) -> dict:
"""Grade one observed day against its own day-of-year +/-7 window.""" """Grade one observed day against its own day-of-year +/-7 window."""
d = _as_date(date) d = _as_date(date)
doy = d.timetuple().tm_yday doy = d.timetuple().tm_yday
doys = df["doy"].to_numpy() sub = _window(df, doy)
sub = df.filter(window_mask(doys, doy))
result = {"date": d.isoformat(), "doy": doy} result = {"date": d.isoformat(), "doy": doy}
for var in TEMP_METRICS: for var in TEMP_METRICS: