

0 / 2 embers
0 / 3000 xp
click for more info
Complete a lesson to start your streak
click for more info
Difficulty: 5
click for more info
Not enough gems
Cost: 6 gems
1: Event Data
incomplete
2: Event Structure
incomplete
3: Event Counting
incomplete
4: Resample
incomplete
5: Resample Aggregations
incomplete
6: Active Users
incomplete
7: Time Patterns
incomplete
8: Funnel Metrics
incomplete
9: Conversion Rates
incomplete
10: Funnel Drop-Offs
incomplete
11: Ordered Funnels
incomplete
12: Cohorts
incomplete
13: Cohort Retention
incomplete
14: Rolling Metrics
incomplete
15: Growth Rates
incomplete
Back
ctrl+,
Next
ctrl+.
This lesson's interactive features are locked, please to keep using them
Raw metrics can be terribly noisy. Mornings are busier than evenings. Holidays create big dips. A sale creates an artificial spike.
Rolling averages smooth out all that noise by averaging the numbers over a moving time window, so you can see the actual trend. The window can be anything: days, weeks, months, etc.
It's not just the value for a single day. It's the average of the values in the window up to that day. So a 3-day rolling average for January 3rd is the average of January 1st, 2nd, and 3rd.
The rolling() method computes a function over a sliding window of N observations. To make each observation one calendar day, include missing dates first:
events["date"] = events["timestamp"].dt.floor("D")
daily_users = events.groupby("date")["user_id"].nunique().asfreq("D", fill_value=0)
# 7-day rolling average
rolling_7d = daily_users.rolling(7).mean()
Which prints something like:
7-day rolling average:
date
2024-01-01 NaN
2024-01-02 NaN
2024-01-03 NaN
2024-01-04 NaN
2024-01-05 NaN
2024-01-06 NaN
2024-01-07 13.000000
2024-01-08 14.142857
2024-01-09 14.857143
...
Name: user_id, dtype: float64
The first 6 values are NaN because there aren't enough prior days to fill the 7-day window! The min_periods parameter can relax that restriction and start calculating the average as soon as there's at least 1 day of data:
rolling_7d = daily_users.rolling(7, min_periods=1).mean()
7-day rolling average:
date
2024-01-01 10.000000
2024-01-02 11.000000
2024-01-03 11.000000
2024-01-04 11.750000
2024-01-05 12.400000
2024-01-06 12.500000
2024-01-07 13.000000
2024-01-08 14.142857
2024-01-09 14.857143
...
Name: user_id, dtype: float64
I default to 7-day rolling averages for daily metrics. It naturally cancels out day-of-week effects. A 28-day window is better for monthly patterns but reacts more slowly to real changes.
SnackStack's dashboards are jittery: raw device temperatures spike and dip too much to read the real trend.
Complete the calculate_rolling_avg function. It accepts an events DataFrame with timestamp and value columns and a window size, and returns a DataFrame sorted by timestamp with a smoothed rolling_avg column.