Rolling Windows¶
Rolling-window accumulation maxima over time series and gridded fields — the basis of an n-hour-maximum verification (24 h maxima being the common case).
Three missing-value conventions, deliberately not unified
These functions differ in how a window containing a gap is treated, and the differences are load-bearing rather than incidental. Picking the wrong one silently changes which window wins.
| function | input | a window containing a gap... |
|---|---|---|
rolling_window_max |
gridded (nt, ny, nx) |
...counts, with gaps read as zero |
rolling_max_valid |
1-D series | ...is disqualified outright |
rolling_window_sums |
1-D series | ...is summed and flagged; the caller decides |
Use the grid convention for gap-free model output, where a NaN means "nothing here" rather than "unknown". Use the series convention for observations, where a gauge window with a missing step must not compete against complete ones on an artificially low total. Use the third when a model series and an observation series must be reduced by the same code, so their window start times cannot drift apart numerically.
Tie-breaking is load-bearing
Zero-padded windows sum to bitwise-identical totals surprisingly often — for a 6-step burst
inside a 72-step record, 19 of 49 24-step windows tie exactly. rolling_window_max resolves a
tie via argmax, max_window via a first-candidate scan within a small absolute tolerance;
they agree only because both scan first-to-last. Reversing either scan direction shifts
reported window start times by many steps.
Example¶
import numpy as np
from modverif.window import rolling_window_sums, max_window, rolling_max_valid
hourly = np.array([...]) # one gauge's hourly totals, NaN where unobserved
# Lenient: gaps count as zero, every window competes.
sums, valid = rolling_window_sums(hourly, 24)
depth, start, spread, clamped = max_window(sums, valid, tol=0.02)
# Strict: any window containing a gap is disqualified.
depth_strict, n_observed = rolling_max_valid(hourly, 24)
spread is the ambiguity diagnostic: over a long event the maximum is often nearly flat, so the
reported start can move by many steps at almost no cost in depth. clamped flags a maximum pinned
against the edge of the record, where the true maximum may lie outside the observed period.
API¶
Rolling-window accumulation maxima over time series and gridded fields.
The event-scale question these answer is "what is the largest N-step accumulation, and when did it start?" -- the basis of an n-hour-maximum precipitation verification (24 h maxima being the common case).
Three missing-value conventions live here, deliberately, and they are not interchangeable.
Unifying them behind a single nan_policy would be a behaviour change wearing the clothes of a
cleanup, so each is a separate function with its convention stated in its own docstring:
============================ ========================= ==================================
function input a window containing a gap...
============================ ========================= ==================================
rolling_window_max gridded (nt, ny, nx) ...counts, with gaps read as zero
rolling_max_valid 1-D series ...is disqualified outright
rolling_window_sums 1-D series ...is summed AND flagged, caller decides
============================ ========================= ==================================
The grid convention suits gap-free model output, where a NaN means "no precipitation recorded here" rather than "unknown". The series convention suits observations, where a gauge window with a missing hour must not compete against complete ones on an artificially low total. The third exists so that a model series and an observation series can be reduced by one implementation -- which is what stops the two sides' window start times from drifting apart numerically.
WARNING:
Tie-breaking is load-bearing and ties are common. Zero-padded windows sum to bitwise-identical
totals surprisingly often -- for a 6-hour burst inside a 72-hour record, 19 of 49 24-hour windows
tie exactly. rolling_window_max resolves a tie via argmax and max_window via a
>= vmax - _TIE_TOL first-candidate scan; they agree only because both scan first-to-last.
Reversing either scan direction shifts reported window start times by hours. Do not "harmonise"
the two rules, and do not reorder the scans.
max_window(sums, valid, tol)
¶
Select the maximal window from rolling_window_sums output, with an ambiguity diagnostic.
The diagnostic is the point: over a long event the maximum is often nearly flat, so the reported
start time can move by hours at almost no cost in depth. spread quantifies that directly, and
clamped flags a maximum pinned against the edge of the record, where the true maximum may lie
outside the observed period.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sums
|
ndarray
|
Rolling window sums. Assumed non-negative -- accumulations of a non-negative quantity,
which is what |
required |
valid
|
ndarray
|
Boolean mask of windows eligible to win. |
required |
tol
|
float
|
Relative depth tolerance defining "near-maximal" for |
required |
Returns:
| Name | Type | Description |
|---|---|---|
max_sum |
float
|
Largest sum over valid windows, or NaN if none are valid. |
start_idx |
int
|
Index of the maximising window, or -1 if none are valid. Ties resolve to the earliest window -- see the module warning; this must not be reordered. |
spread |
float
|
Index range spanned by valid windows within |
clamped |
bool
|
True if the maximising window sits at the first or last available position, i.e. the record may be cutting the event off. |
rolling_max_valid(series, window)
¶
Maximum rolling window-step sum of a 1-D series, ignoring windows that contain a gap.
A window with any missing step is disqualified rather than summed over what is present -- an incomplete gauge window would otherwise compete on an artificially low total and could win.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
series
|
ndarray
|
1-D per-step increments. Non-finite entries mark gaps. |
required |
window
|
int
|
Number of consecutive steps per accumulation window. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
max_sum |
float
|
Largest sum over fully-observed windows, or NaN if the series is shorter than one window or no window is complete. |
n_valid |
int
|
Count of finite steps in the whole series -- a coverage diagnostic, reported even when no complete window exists. |
rolling_window_max(field, window)
¶
Per-cell maximum rolling window-step sum over the time axis of a gridded field.
Missing values are read as zero, so every window competes. This suits gap-free model output,
where a NaN means "nothing here" rather than "unknown"; for observations, where a gap must
disqualify its window, use rolling_max_valid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
field
|
ndarray
|
Per-step increments, shape |
required |
window
|
int
|
Number of consecutive time steps per accumulation window. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
max_grid |
ndarray
|
Maximum window sum per cell, shape |
start_idx |
ndarray
|
Time index of the first step of each cell's maximising window, shape |
rolling_window_sums(series, window)
¶
Every rolling window-step sum of a 1-D series, plus which windows are fully observed.
Reports both conventions instead of choosing: sums treat gaps as zero (the
rolling_window_max grid rule), while valid marks the all-finite windows (the
rolling_max_valid observation rule). One implementation can therefore reduce a model
series and an observation series, which is what keeps the two sides' window start times from
drifting apart numerically. Pair with max_window to select.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
series
|
ndarray
|
1-D per-step increments. Non-finite entries mark gaps. |
required |
window
|
int
|
Number of consecutive steps per accumulation window. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
sums |
ndarray
|
Rolling sums, shape |
valid |
ndarray
|
Boolean, same shape, True where the window contains no gap. |