Python · medium

Given a pandas DataFrame, compute the 30-day moving average of a column.

Asked in Data Scientist interviews, in the Python round.

Short answer

df['ma'] = df['x'].rolling(30).mean().

Sample data

These tables are loaded into an in-browser engine on this page, so you can run your own answer against them. No account needed.

prices

price_dateprice
2026-06-01101
2026-06-02103
2026-06-0399
2026-06-04104
2026-06-05108
2026-06-06105
2026-06-07110
2026-06-08107
2026-06-09112
2026-06-10109
2026-06-11115
2026-06-12111
2026-06-13118
2026-06-14114

How to answer it

Definition

The average of a column over the current row and the 29 before it. In pandas that is .rolling(), and the question is really about what "30" counts: rows, or days.

Syntax

df["ma_30"] = df["value"].rolling(window=30).mean()          # 30 ROWS
df["ma_30"] = df["value"].rolling("30D").mean()              # 30 DAYS

Example

import pandas as pd

df = df.sort_values("date").set_index("date")

# One row per day first, so a row and a day mean the same thing
daily = df["revenue"].resample("D").sum()
ma = daily.rolling("30D").mean()
print(ma.tail(3))

Output

date
2025-03-29    41832.4
2025-03-30    41905.1
2025-03-31    42017.8
Freq: D, Name: revenue, dtype: float64

Rows or days

rolling(30) counts 30 rows. If the frame has gaps, or several rows per day, that is not 30 days. rolling("30D") counts a time offset and needs a DatetimeIndex, which is why the example sets one. Resampling to a daily grain first makes the two equivalent and removes the ambiguity entirely.

Key points

FAQ

The 30-second answer

Sort by date, then rolling thirty and take the mean.
The thing I would clarify before writing it is whether thirty means thirty rows or thirty days, because rolling with a number counts rows. If the data has gaps, or more than one row per day, those are different answers, so I would either resample to a daily grain first or use the offset form with "30D", which needs a DatetimeIndex.
I would also mention that the first twenty-nine values come back NaN by design, and that filling them through min_periods is a choice to make deliberately rather than to default into.

In the interview

Practice this for real