Skip to content

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 rolling_window_sums produces. With a negative maximum and tol > 0, (1 - tol) * vmax exceeds vmax, no window qualifies as near-maximal, and spread raises on the empty selection. Unreachable for precipitation; stated because this is public API and the assumption is otherwise invisible.

required
valid ndarray

Boolean mask of windows eligible to win.

required
tol float

Relative depth tolerance defining "near-maximal" for spread; e.g. 0.02 counts every window within 2 % of the maximum.

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 tol of the maximum: how far the reported start could move at less than tol cost in depth. NaN if no window is valid.

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 (nt, ny, nx). NaNs are treated as 0.

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 (ny, nx), float32.

start_idx ndarray

Time index of the first step of each cell's maximising window, shape (ny, nx). Ties resolve to the earliest window (argmax semantics) -- see the module warning.

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 (len(series) - window + 1,), gaps summed as zero.

valid ndarray

Boolean, same shape, True where the window contains no gap.