Records: show longest dry streak instead of record-low precipitation (#105)

Record-low rainfall is meaningless (always ~0), so the precip row's low side now
shows the longest run of consecutive days without measurable rain and the date that
dry spell began (grading.longest_dry_streak), keeping the wettest day on the high
side. A footnote explains the substitution.
This commit is contained in:
Emi Griffith 2026-07-15 20:28:26 -07:00 committed by GitHub
parent 0e16ad2a93
commit 5f380bcadd
3 changed files with 36 additions and 1 deletions

View file

@ -347,10 +347,18 @@ def _records_context(request, city, history) -> dict:
r = rec.get(key)
if not r:
continue
if key == "precip":
# "Record low" rain is meaningless (it's just 0), so the low side shows
# the longest dry streak and the date it began instead.
days, dry_start = grading.longest_dry_streak(history)
low = f"{days}-day dry spell" if days else ""
low_date = dry_start or ""
else:
low, low_date = _fmt(key, r["min"]), r["min_date"]
rows.append({
"label": label,
"high": _fmt(key, r["max"]), "high_date": r["max_date"],
"low": _fmt(key, r["min"]), "low_date": r["min_date"],
"low": low, "low_date": low_date,
})
breadcrumb = [("Home", f"{BASE}/"), ("Climate", f"{BASE}/climate"),
(name, f"{BASE}/climate/{city['slug']}"), ("Records", None)]

View file

@ -182,6 +182,30 @@ def all_time_records(df: pl.DataFrame) -> dict:
return out
def longest_dry_streak(df: pl.DataFrame) -> tuple[int, str | None]:
"""Longest run of consecutive days without measurable rain, and the ISO date the
streak began. A dry day is precip < RAIN_THRESHOLD; null precip counts as dry."""
if "precip" not in df.columns:
return (0, None)
d = df.select(["date", "precip"]).sort("date")
dates, precips = d["date"].to_list(), d["precip"].to_list()
best_len, best_start = 0, None
cur_len, cur_start = 0, None
for dt, p in zip(dates, precips):
wet = p is not None and not (isinstance(p, float) and np.isnan(p)) and p >= RAIN_THRESHOLD
if wet:
cur_len, cur_start = 0, None
else:
if cur_len == 0:
cur_start = dt
cur_len += 1
if cur_len > best_len:
best_len, best_start = cur_len, cur_start
start = best_start.isoformat() if best_start and hasattr(best_start, "isoformat") else (
str(best_start) if best_start else None)
return (best_len, start)
def _band_stats(samples: np.ndarray) -> dict | None:
if samples.size == 0:
return None

View file

@ -26,6 +26,9 @@
</tbody>
</table>
</div>
<p class="records-note muted">Rainfall has no meaningful record low, so its “record low”
column shows the <b>longest run of consecutive days without measurable rain</b> and the
date that dry spell began.</p>
<p><a class="cta" href="{{ base }}/#{{ '%.5f,%.5f' | format(city.lat, city.lon) }}">Grade {{ name }}'s weather now →</a></p>
<p class="climate-foot muted"><a href="{{ base }}/climate/{{ city.slug }}"> Back to {{ name }} climate</a></p>