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
Steps to calculate pandas rolling windows:
- Save the rolling-window demo script.
- rolling_window_demo.py
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.
- Run the script.
$ 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 - Sort the records before the rolling calculation.
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.
- Create the fixed-row rolling calculation for the numeric column.
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.
- Add rolling mean and total columns to the DataFrame.
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 one row against the source values.
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.
- Use a time-offset window when calendar distance matters more than row count.
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.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.