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.

Steps to calculate pandas rolling windows:

  1. 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.

  2. 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
  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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.