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:
parent
0e16ad2a93
commit
5f380bcadd
3 changed files with 36 additions and 1 deletions
10
content.py
10
content.py
|
|
@ -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)]
|
||||
|
|
|
|||
24
grading.py
24
grading.py
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue