Dates and Ordered Data
Rolling Mean (Window = 3)
Compute a 3-element sliding-window average over an ordered numeric sequence.
By hand, the first two positions cannot form a full window so they get None;
positions 2 onward average the current element and the two before it. With
pandas, Series.rolling(3).mean() handles the window and fills the incomplete
prefix with NaN.
By hand
Walk range(len(readings)). Indices 0 and 1 have fewer than 3 elements
available, so append None. From index 2 onward, slice the 3-element window
readings[i - 2: i + 1] into w, compute round(sum(w) / 3, 2), and
append. The trace shows w refreshing each iteration alongside the growing
result.
readings = [6, 9, 12, 9, 15, 9]
result = []
for i in range(len(readings)):
if i < 2:
result.append(None)
else:
w = readings[i - 2: i + 1]
result.append(round(sum(w) / 3, 2))
print('RESULT:', result)
readings ← [6, 9, 12, 9, 15, 9]
1readings = [6, 9, 12, 9, 15, 9]2result = []values this step[6, 9, 12, 9, 15, 9]readingsresult ← []
1readings = [6, 9, 12, 9, 15, 9]2result = []3for i in range(len(readings)):values this step[]resulti ← 0
2result = []3for i in range(len(readings)):4 if i < 2:values this step0iif i < 2:
3for i in range(len(readings)):4 if i < 2:5 result.append(None)result ← [None]
4if i < 2:5 result.append(None)6else:values this step[] → [None]resulti ← 1
2result = []3for i in range(len(readings)):4 if i < 2:values this step0 → 1iif i < 2:
3for i in range(len(readings)):4 if i < 2:5 result.append(None)result ← [None, None]
4if i < 2:5 result.append(None)6else:values this step[None] → [None, None]resulti ← 2
2result = []3for i in range(len(readings)):4 if i < 2:values this step1 → 2iif i < 2:
3for i in range(len(readings)):4 if i < 2:5 result.append(None)w ← [6, 9, 12]
6else:7 w = readings[i - 2: i + 1]8 result.append(round(sum(w) / 3, 2))values this step[6, 9, 12]wresult ← [None, None, 9.0]
7 w = readings[i - 2: i + 1]8 result.append(round(sum(w) / 3, 2))9print('RESULT:', result)values this step[None, None] → [None, None, 9.0]resulti ← 3
2result = []3for i in range(len(readings)):4 if i < 2:values this step2 → 3iif i < 2:
3for i in range(len(readings)):4 if i < 2:5 result.append(None)w ← [9, 12, 9]
6else:7 w = readings[i - 2: i + 1]8 result.append(round(sum(w) / 3, 2))values this step[6, 9, 12] → [9, 12, 9]wresult ← [None, None, 9.0, 10.0]
7 w = readings[i - 2: i + 1]8 result.append(round(sum(w) / 3, 2))9print('RESULT:', result)values this step[None, None, 9.0] → [None, None, 9.0, 10.0]resulti ← 4
2result = []3for i in range(len(readings)):4 if i < 2:values this step3 → 4iif i < 2:
3for i in range(len(readings)):4 if i < 2:5 result.append(None)w ← [12, 9, 15]
6else:7 w = readings[i - 2: i + 1]8 result.append(round(sum(w) / 3, 2))values this step[9, 12, 9] → [12, 9, 15]wresult ← [None, None, 9.0, 10.0, 12.0]
7 w = readings[i - 2: i + 1]8 result.append(round(sum(w) / 3, 2))9print('RESULT:', result)values this step[None, None, 9.0, 10.0] → [None, None, 9.0, 10.0, 12.0]resulti ← 5
2result = []3for i in range(len(readings)):4 if i < 2:values this step4 → 5iif i < 2:
3for i in range(len(readings)):4 if i < 2:5 result.append(None)w ← [9, 15, 9]
6else:7 w = readings[i - 2: i + 1]8 result.append(round(sum(w) / 3, 2))values this step[12, 9, 15] → [9, 15, 9]wresult ← [None, None, 9.0, 10.0, 12.0, 11.0]
7 w = readings[i - 2: i + 1]8 result.append(round(sum(w) / 3, 2))9print('RESULT:', result)values this step[None, None, 9.0, 10.0, 12.0] → [None, None, 9.0, 10.0, 12.0, 11.0]resultfor i in range(len(readings)):
2result = []3for i in range(len(readings)):4 if i < 2:stdout ← RESULT: [None, None, 9.0, 10.0, 12.0, 11.0]
8 result.append(round(sum(w) / 3, 2))9print('RESULT:', result)values this stepRESULT: [None, None, 9.0, 10.0, 12.0, 11.0]stdout
With pandas
df['x'].rolling(3).mean() computes the mean of each 3-row window in one
call. The first two rows have no complete window, so they are NaN and the
column is float64. The snapshot shows the raw output; result converts
NaN → None and rounds the finite values for comparison with the naive half.
import math
import pandas as pd
from dalib.display import set_display
set_display()
readings = [6, 9, 12, 9, 15, 9]
df = pd.DataFrame({'x': readings})
r = df['x'].rolling(3).mean()
result = [round(v, 2) if not math.isnan(v) else None for v in r.tolist()]
print('index:', r.index.tolist())
print('dtype:', r.dtype)
print('values raw:', r.tolist())
print('RESULT:', result)
index: [0, 1, 2, 3, 4, 5]
dtype: float64
values raw: [nan, nan, 9.0, 10.0, 12.0, 11.0]
RESULT: [None, None, 9.0, 10.0, 12.0, 11.0]
Implementation notes
- The
min_periodsparameter controls how many non-NaN values are required to compute a result.rolling(3, min_periods=1)fills the prefix instead of returningNaN. rollingalso supportssum(),min(),max(),std(), andapply(fn)for custom window functions.- The
float64upcast andNaNprefix mirrordiff()— usemath.isnanorpd.isnawhen normalising for comparison. - Cross-reference:
diff-previous-row(this chapter) for a single-step lag instead of an aggregated window. - Cross-reference:
running-total(python-data-basics) for the cumulative-sum approach — accumulating all prior values rather than a fixed-size window.