From 8cec935b0606b53e69293d10fd47557b4928e582 Mon Sep 17 00:00:00 2001 From: Emi Griffith Date: Wed, 22 Jul 2026 12:08:03 -0700 Subject: [PATCH] Port 983bceb Extract the grading day-of-year window helpers from monorepo (drift reconciliation) Claude-Session: https://claude.ai/code/session_01RdARHDJaYC1wSQRTYM6t3Z --- data/grading.py | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/data/grading.py b/data/grading.py index 11c2c8f..6f8f350 100644 --- a/data/grading.py +++ b/data/grading.py @@ -144,6 +144,21 @@ def window_mask(doys: np.ndarray, target_doy: int, half: int = HALF_WINDOW) -> n 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: """Mid-rank percentile of `value` within `samples` (handles ties correctly).""" 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: """Summarize the +/-7 day historical distribution for one day of the year.""" - doys = df["doy"].to_numpy() - sub = df.filter(window_mask(doys, target_doy)) - 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, - } + sub = _window(df, target_doy) + out = {"target_doy": int(target_doy), **_window_meta(sub)} for var in CLIMO_METRICS: if var not in sub.columns: 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.""" d = _as_date(date) doy = d.timetuple().tm_yday - sub = df.filter(window_mask(df["doy"].to_numpy(), doy)) - years = sub["date"].dt.year() + sub = _window(df, doy) metrics = {} for var in TEMP_METRICS: @@ -420,8 +428,7 @@ def day_detail(df: pl.DataFrame, date, obs: dict | None) -> dict: return { "date": d.isoformat(), "doy": doy, - "n_samples": int(len(sub)), - "year_range": [int(years.min()), int(years.max())] if len(sub) else None, + **_window_meta(sub), "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.""" d = _as_date(date) doy = d.timetuple().tm_yday - doys = df["doy"].to_numpy() - sub = df.filter(window_mask(doys, doy)) + sub = _window(df, doy) result = {"date": d.isoformat(), "doy": doy} for var in TEMP_METRICS: