analysis.welch_moving_average

analysis.welch_moving_average(series_by_run, window=None, *, method='welch')

Smooth an ensemble-averaged series to visually locate a warm-up length.

Underlies plot_warm_up_diagnostic. Takes one series per replication - e.g. queue length at each snapshot, resource occupancy at each snapshot, or per-entity durations in arrival order - and reduces the replications to a single smoothed curve whose early instability (or lack of it) is the visual signal a modeller reads off to choose warm_up=.

Parameters

Name Type Description Default
series_by_run sequence of sequence of float One series per replication, each already in the order its warm-up length will be judged against (snapshot time order, or entity arrival order). Runs of unequal length are truncated to the shortest, with a warning - the ensemble average at index i needs a value from every run at that index, so a longer run contributes no extra information past the shortest run’s end. required
window int Half-width of the moving-average window. Required, and must be a positive integer less than the (truncated) series length, when method="welch". Ignored when method="cumulative" or method="none", neither of which has a window. None
method (welch, cumulative, none) - "welch": Welch’s (1983) moving-average procedure, as described in Law, Simulation Modeling and Analysis. The ensemble average is smoothed with a symmetric window of full width 2 * window + 1; for the first window points, where a full-width window would run off the start of the series, it shrinks to the narrower symmetric window that does exist (2i - 1 points, i.e. only the i - 1 points actually available on each side - i here is Welch’s own 1-indexed position in the series, i = 1 being the first point, not this function’s 0-indexed output array) rather than returning NaN there - this is the detail a pandas.rolling(center=True) call gets wrong. A window this wide also has no full-width neighbourhood in the last window points of the series, so those are not returned at all: the output has length len(series) - window, not len(series) - a larger window gives a smoother curve at the cost of a shorter one. - "cumulative": the plain expanding mean of the ensemble average, mean(ensemble[:i + 1]) for every i - simpler and needs no window, but each new point only shifts the running mean by 1/i, so a biased early transient decays out of it far more slowly than out of Welch’s fixed-width window. The risk is not that this curve looks rougher - it is smoother, in a way that can make it look settled long after (or hide a real shift long before) the series has actually stabilised. This is the “cumulative mean” time series inspection technique demonstrated in the DES RAP book (Heather et al., 2026 - https://pythonhealthdatascience.github.io/des_rap_book/pages/guide/output_analysis/length_warmup.html), which plots both the pooled cumulative mean and each replication’s own cumulative mean (matching show_runs=True below) and cites Robinson, S. (2004), Simulation: The Practice of Model Development and Use (Wiley), for the general “look for where the curve stabilises” principle. Returned at the series’ full length. - "none": the ensemble average itself, completely unsmoothed - no window, no running mean, nothing. A further step beyond the cumulative-mean inspection above - here dropping the running-mean smoothing entirely rather than only the fixed window - not itself the specific technique the DES RAP book demonstrates. Noisier than either other method by construction, since nothing here reduces the within-replication variance the ensemble average didn’t already remove; useful specifically when you want to see that noise rather than have it smoothed away. Returned at the series’ full length, identical to what _ensemble_mean computes. "welch"

Returns

Name Type Description
numpy.ndarray The smoothed series. See method above for its length.

Raises

Name Type Description
ValueError If series_by_run is empty; if method is not "welch", "cumulative" or "none"; or if method="welch" and window is None, not a positive integer, or >= the (truncated) series length, leaving nothing to return.

Notes

Deliberately has no automatic warm-up-length selector. Welch’s procedure is explicitly a visual one - a flatness threshold returns a confident number that is wrong on any series with slow drift, silently discarding the wrong amount of data with no signal anything went awry. Overlay several window values (see plot_warm_up_diagnostic) and read off the point where they agree, by eye.

See Also

plot_warm_up_diagnostic : Builds the figure this function’s output is drawn into.

Back to top