Written and maintained by CASRAI Editorial Board
Last updated
Short answer: DataFrame.rolling() (and Series.rolling()) in pandas slides a fixed-size or time-based window along your data and lets you apply an aggregation such as mean(), sum() or std() to each window position. This guide is for researchers and analysts who need to smooth a measurement series, compute a moving statistic or build a time-aware feature, and who want to know exactly why the first rows come back as NaN, what min_periods, center and closed change, and how to handle irregular timestamps. Behaviour below follows the pandas 3.1.1 documentation (read 2026-11-07) and was run on pandas 3.0.5.
What pandas rolling is used for in research
A rolling calculation produces one output value per input row, computed from the rows inside a moving window. Typical research uses are smoothing a noisy instrument signal, tracking a moving mean or standard deviation of a repeated measurement, computing a moving correlation between two series, and building lagged summary features for time-series models. It is the pandas counterpart of the idea explained in what a moving average is; the same concept in another environment is covered in moving averages in MATLAB.
Rolling windows are one of three window types in the pandas windowing guide. rolling() gives every observation in the window equal weight (unless you pass win_type), expanding() grows the window from the first observation, and ewm() weights observations with exponentially decaying weights and has no window size at all.
Install or access
pandas is installed from the official project sources. According to the pandas installation page (read 2026-11-07):
pip install pandas(PyPI)conda install -c conda-forge pandas(conda-forge), or create a fresh environment withconda create -c conda-forge -n name_of_my_env python pandas
The page lists NumPy 2.0.2 as the minimum required NumPy version. Check your installed version with pandas.__version__ before comparing outputs with the documentation.
Step-by-step: build a rolling calculation
1. The signature
The documented signature is DataFrame.rolling(window, min_periods=None, center=False, win_type=None, on=None, closed=None, step=None, method='single'). It returns a Rolling object (or a Window object when win_type is passed). Nothing is computed until you call an aggregation on it.
2. Choose the window
window can be an integer (number of observations), a time offset such as '2D' or '3s' (variable-size window, datetime-like index only, and the offset must be a fixed frequency), or a subclass of BaseIndexer for custom windows.
import pandas as pd
s = pd.Series(
[4.0, 6.0, 5.0, 9.0, 7.0, 8.0],
index=pd.to_datetime(["2026-01-01", "2026-01-02", "2026-01-03",
"2026-01-06", "2026-01-07", "2026-01-08"]),
name="x",
)
s.rolling(3).mean()
3. Apply an aggregation
Call mean(), sum(), median(), min(), max(), std() or var(), or several at once with agg(["mean", "std"]). For anything else use apply(func, raw=...): raw=True passes each window as an ndarray, raw=False passes a Series, and engine='numba' requires raw=True. Results are float64, and the aggregations apply to numeric data. For two series, cov() and corr() give rolling covariance and correlation (with pairwise=True for all column pairs).
4. Decide how to handle the edges and missing values
Set min_periods, center and closed as described in the decision table below.
Parameter decision table
Every row below comes from the pandas DataFrame.rolling documentation (pandas 3.1.1, read 2026-11-07).
| Parameter | Default | What it does |
|---|---|---|
window |
required | Integer = number of observations. Offset string or timedelta = variable-size time window (datetime-like index, fixed frequency only). BaseIndexer subclass = custom window. |
min_periods |
None |
Minimum observations needed to produce a value. For an integer window it defaults to the window size; for an offset window it defaults to 1. |
center |
False |
False labels the result at the right edge of the window; True labels it at the window centre. |
win_type |
None |
None gives equal weights. A string names a valid scipy.signal window (for example "gaussian", which needs a parameter such as std in the aggregation: df.rolling(2, win_type="gaussian").sum(std=3)). |
on |
None |
Column label to roll over instead of the index. |
closed |
None (acts as 'right') |
Which window endpoints are included: 'right', 'left', 'both' or 'neither'. |
step |
None |
Evaluate every n-th row, like slicing with [::step]. The window must be an integer. |
method |
'single' |
'table' applies the function to the whole table and is available only with engine='numba'. |
Output and how to read it
Why are the first rows NaN?
With an integer window and the default min_periods, a value is produced only when the window is full. s.rolling(3).mean() on the series above returns NaN, NaN, 5.0, 6.667, 7.0, 8.0: the first two rows have fewer than three observations. Setting min_periods=1 returns a value from the first row using whatever observations are available. The documentation example df = pd.DataFrame({"B": [0, 1, 2, np.nan, 4]}) shows the effect: df.rolling(2).sum() gives NaN, 1.0, 3.0, NaN, NaN, while df.rolling(2, min_periods=1).sum() gives 0.0, 1.0, 3.0, 2.0, 4.0.
What happens to missing data inside the window?
NaN values are not filled. They are excluded from the count of observations in the window, and min_periods decides whether the remaining observations are enough. In the example above, the NaN at position 3 makes rolling(2).sum() return NaN for that row and the next, because neither window holds two valid values; with min_periods=1 each of those windows sums its one valid value.
What does center do?
By default the result is labelled at the right edge of the window, so it describes the past. With center=True the label is the window centre. In the documentation example, df.rolling(3, min_periods=1, center=True).sum() gives 1.0, 3.0, 3.0, 6.0, 4.0 while center=False gives 0.0, 1.0, 3.0, 3.0, 6.0. On the worked series, s.rolling(3, center=True).mean() returns NaN in both the first and last rows, because an edge window of three centred values is incomplete at each end.
Time-based windows on irregular timestamps
Research data is often measured at uneven times. An offset window counts time, not rows. On the worked series (note the gap between 3 and 6 January), s.rolling("3D").mean() returns 4.0, 5.0, 5.0, 9.0, 8.0, 8.0: on 6 January the 3-day window contains only that day’s value, so the mean is 9.0. By default offset windows use min_periods=1, so no leading NaN appears. The time index must be monotonic.
Offset windows must be fixed frequencies. Calendar offsets such as 'B' (business day) or 'ME' (month end) raise a ValueError; in the version tested, the message reads that the offset “is a non-fixed frequency”. Use a fixed span such as '30D' instead.
To roll over a datetime column rather than the index, pass on: df.rolling("2D", on="A").sum().
Avoiding look-ahead with closed
For prediction features, the current row should not contribute to its own window. The pandas windowing guide notes that closed='left' or 'neither' helps avoid look-ahead. On the worked series, s.rolling("3D", closed="left").mean() returns NaN, 4.0, 5.0, 5.0, 9.0, 8.0: each value summarises only earlier observations.
Forward-looking windows
A rolling window looks backward by default. For a forward-looking window use an indexer: indexer = pd.api.indexers.FixedForwardWindowIndexer(window_size=2) followed by df.rolling(window=indexer, min_periods=1).sum(), which on the documentation example gives 1.0, 3.0, 2.0, 4.0, 4.0. A negative offset string does not create a forward window. For time-based forward windows the documentation reverses the data, rolls, then reverses back: df_time.iloc[::-1].rolling("3s").sum().iloc[::-1].
Rolling vs expanding vs ewm
| rolling() | expanding() | ewm() | |
|---|---|---|---|
| Window | Fixed size or time span | Grows from the first observation | No window size |
| Weights | Equal (or a scipy window via win_type) |
Equal | Exponentially decaying; set exactly one of alpha, span, com or halflife |
| On the worked series, mean | NaN, NaN, 5.0, 6.667, 7.0, 8.0 (window 3) |
4.0, 5.0, 5.0, 6.0, 6.2, 6.5 |
4.0, 5.333, 5.143, 7.2, 7.097, 7.556 (span=3) |
For irregular sampling, ewm accepts a times argument that works with halflife. For more on a statistical-modelling alternative to hand-built smoothing, see the statsmodels API guide.
Troubleshooting
- Result is all NaN: the window is larger than the number of valid observations. Reduce
windowor setmin_periods. - ValueError about a non-fixed frequency: you passed
'B','ME'or a similar calendar offset. Use a fixed span in days, hours or seconds. - Time-window error on an unsorted index: offset windows need a monotonic datetime index. Sort the index first.
- Step with an offset window:
steprequires an integer window. - Gaussian window complaint:
win_type="gaussian"needsstdpassed to the aggregation. - Combining datasets before rolling: align and join the series first; see pandas concat.
Free alternatives
| Tool | Equivalent | Notes (from the tool’s own documentation) |
|---|---|---|
| Polars (Python) | Expr.rolling_mean(window_size, weights=None, *, min_samples=None, center=False) |
Window length is counted in elements; min_samples defaults to the window size. |
| R, zoo package | rollapply(data, width, FUN, ..., by = 1, fill, partial = FALSE, align = c("center", "left", "right")) |
Default alignment is centred; rollmean is documented as an optimised variant for means. |
| NumPy | numpy.lib.stride_tricks.sliding_window_view |
Returns a view of all windows; apply a reduction along the last axis, for example .mean(axis=1). It has no NaN padding or time-based windows. |
Note the different defaults: pandas labels at the right edge unless center=True, whereas zoo defaults to centred alignment. Check this when comparing results across tools. For interpolation of gaps before smoothing, see interpolation in NumPy.
FAQ
What does pandas rolling mean?
It means a moving window over the data: for each row, pandas takes the observations in a window ending at (or centred on) that row and applies a function to them.
How do I compute a rolling mean in pandas?
Call df["col"].rolling(window).mean(), for example s.rolling(3).mean(). Add min_periods=1 to avoid leading NaN rows.
How do I get a rolling max or rolling difference?
Use rolling(3).max() for the maximum (on the worked series: NaN, NaN, 6.0, 9.0, 9.0, 9.0). For the change across each window, use apply: s.rolling(3).apply(lambda w: w.iloc[-1] - w.iloc[0], raw=False) returns NaN, NaN, 1.0, 3.0, 2.0, -1.0. For a plain row-to-row difference, diff() does not need a window.
How do I get the rolling last value?
Use apply with raw=True, for example s.rolling(3).apply(lambda w: w[-1], raw=True), which passes each window as an ndarray.
How is rolling different from ewm?
Rolling weights every observation in a fixed window equally; ewm weights all earlier observations with exponentially decaying weights and has no window size.
Can pandas rolling look forward?
Yes, with FixedForwardWindowIndexer or by reversing the data; negative offset strings do not work.
Sources
- pandas documentation, pandas.DataFrame.rolling (pandas 3.1.1), read 2026-11-07.
- pandas documentation, Windowing operations, read 2026-11-07.
- pandas documentation, Installation, read 2026-11-07.
- Polars documentation, Expr.rolling_mean, read 2026-11-07.
- CRAN, zoo reference manual, read 2026-11-07.
- Worked-series outputs were run on pandas 3.0.5 and NumPy 2.5.1 on 2026-11-07.







