Rolling windows in pandas calculate statistics across the current row and a fixed number of nearby rows. They are useful for moving averages, rolling totals, and short-term trend checks when data is already ordered by time, sequence, or another meaningful key.
DataFrame.rolling() creates a rolling object, and functions such as mean(), sum(), count(), and agg() turn each window into output values. An integer window counts rows, labels each result at the right edge by default, and returns NaN until enough observations are present unless min_periods allows an earlier result.
Sort the DataFrame before calculating windows so each output row uses the intended previous records. Time-offset windows such as ā3Dā use a datetime-like index or an on column, which makes the row count per window vary when dates are irregular.
Related: How to create a pandas DataFrame
Related: How to find missing values in pandas
import pandas as pd orders = pd.DataFrame( { "date": pd.to_datetime( [ "2026-01-01", "2026-01-02", "2026-01-03", "2026-01-04", "2026-01-05", "2026-01-06", ] ), "orders": [18, 22, 17, 25, 29, 24], "sales_usd": [420.0, 510.0, 460.0, 590.0, 640.0, 610.0], } ) orders = orders.sort_values("date").set_index("date") result = orders.assign( sales_mean_3row=orders["sales_usd"] .rolling(window=3, min_periods=2) .mean() .round(2), sales_total_3row=orders["sales_usd"].rolling(window=3, min_periods=2).sum(), ) print("fixed-row rolling window") print(result.to_string()) print() check_date = pd.Timestamp("2026-01-05") window_values = orders.loc["2026-01-03":"2026-01-05", "sales_usd"] print("window values for 2026-01-05:", window_values.tolist()) print("verified average:", result.loc[check_date, "sales_mean_3row"]) print("verified total:", result.loc[check_date, "sales_total_3row"]) assert result.loc[check_date, "sales_mean_3row"] == round(window_values.mean(), 2) assert result.loc[check_date, "sales_total_3row"] == window_values.sum() irregular = pd.DataFrame( { "date": pd.to_datetime( ["2026-01-01", "2026-01-02", "2026-01-05", "2026-01-06"] ), "sales_usd": [420.0, 510.0, 640.0, 610.0], } ) time_window = irregular.assign( sales_total_3d=irregular.rolling( window="3D", on="date", min_periods=1 )["sales_usd"].sum() ) print() print("time-offset rolling window") print(time_window.to_string(index=False)) print() print("verification: rolling windows match source rows")
window=3 uses the current row and the previous two rows. min_periods=2 prints a result after two valid rows instead of waiting for all three.
$ python3 rolling_window_demo.py
fixed-row rolling window
orders sales_usd sales_mean_3row sales_total_3row
date
2026-01-01 18 420.0 NaN NaN
2026-01-02 22 510.0 465.00 930.0
2026-01-03 17 460.0 463.33 1390.0
2026-01-04 25 590.0 520.00 1560.0
2026-01-05 29 640.0 563.33 1690.0
2026-01-06 24 610.0 613.33 1840.0
window values for 2026-01-05: [460.0, 590.0, 640.0]
verified average: 563.33
verified total: 1690.0
time-offset rolling window
date sales_usd sales_total_3d
2026-01-01 420.0 420.0
2026-01-02 510.0 930.0
2026-01-05 640.0 640.0
2026-01-06 610.0 1250.0
verification: rolling windows match source rows
orders = orders.sort_values("date").set_index("date")
Rolling windows follow the current row order. Sorting by date first prevents a moving average from mixing records in import, merge, or database-return order.
orders["sales_usd"].rolling(window=3, min_periods=2)
The first row is NaN because only one value is available. Later rows use the current sales_usd value plus up to two previous values.
result = orders.assign( sales_mean_3row=orders["sales_usd"] .rolling(window=3, min_periods=2) .mean() .round(2), sales_total_3row=orders["sales_usd"].rolling(window=3, min_periods=2).sum(), )
Rolling aggregation output is numeric and displays floating-point values, so totals such as 930.0 are expected even when the source values look like whole numbers.
check_date = pd.Timestamp("2026-01-05") window_values = orders.loc["2026-01-03":"2026-01-05", "sales_usd"] assert result.loc[check_date, "sales_mean_3row"] == round(window_values.mean(), 2) assert result.loc[check_date, "sales_total_3row"] == window_values.sum()
The 2026-01-05 row uses 460.0, 590.0, and 640.0, so the rolling mean is 563.33 and the rolling total is 1690.0.
time_window = irregular.assign( sales_total_3d=irregular.rolling( window="3D", on="date", min_periods=1 )["sales_usd"].sum() )
window=ā3Dā uses the rows inside the three-day datetime window for each record. On 2026-01-05, the earlier 2026-01-02 record is outside that window, so the total is 640.0.