Pandas: Window Functions
Last updated: 2026-08-26
Lesson 17 introduced rolling/expanding/ewm. This lesson goes deeper: custom window aggregation, advanced time windows, ewm parameter relationships, and groupby+rolling combinations. Window functions are a core tool for quantitative analysis and sensor data processing — master them, and you master the ability to "see trends through a sliding lens."
1. What You Will Learn
- ❶ Advanced rolling parameters
- ❷ Custom window aggregation
- ❸ expanding cumulative windows in depth
- ❹ ewm parameters explained
- ❺ groupby + rolling
2. Carol's Exercise Heart Rate Window Analysis
(1) The Problem: A 30-Second Window Mean Is Not Enough
Carol's exercise heart rate data needs richer window statistics — not just the mean, but also the maximum and heart rate variability (standard deviation):
import pandas as pd
import numpy as np
np.random.seed(42)
hr = pd.DataFrame({
'heart_rate': np.random.normal(75, 10, 60).round(0)
}, index=pd.date_range('2024-01-15 08:00', periods=60, freq='30s'))
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.
(2) The Solution: rolling with Multiple Aggregations
▶ Example: rolling Multi-Metric Statistics (Difficulty ⭐)
import pandas as pd
import numpy as np
np.random.seed(42)
hr = pd.DataFrame({
'heart_rate': np.random.normal(75, 10, 60).round(0)
}, index=pd.date_range('2024-01-15 08:00', periods=60, freq='30s'))
# Rolling with multiple aggregations
stats = hr['heart_rate'].rolling(10).agg(['mean', 'std', 'min', 'max'])
stats.columns = ['hr_ma', 'hr_std', 'hr_min', 'hr_max']
print(stats.dropna().head(5).round(1))
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.
3. Advanced rolling
(1) Key Parameters
▶ Example
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.
: rolling Parameter Reference (Difficulty ⭐⭐)
import pandas as pd
import numpy as np
np.random.seed(42)
s = pd.Series(np.random.randn(20).cumsum() + 100)
# min_periods: allow partial windows
print(s.rolling(5, min_periods=2).mean().head(5))
# center: centered window (for smoothing)
print(s.rolling(5, center=True).mean().head(5))
# win_type: weighted window (Gaussian, triangular, etc.)
print(s.rolling(5, win_type='gaussian').mean(std=1.5).head(8))
# on: use a column as time (when not the index)
df = pd.DataFrame({
'timestamp': pd.date_range('2024-01', periods=20, freq='6h'),
'value': np.random.randn(20).cumsum()
})
print(df.rolling('12h', on='timestamp')['value'].mean().head(5))
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.
(2) rolling Parameter Quick Reference
| Parameter | Default | Description |
|---|---|---|
| window | Required | Window size (integer row count or time string) |
| min_periods | window | Minimum observations required in the window |
| center | False | Center the window |
| win_type | None | Weighting type (gaussian/triangular, etc.) |
| on | None | Use a column as the time axis (instead of Index) |
| closed | 'right' | Which end of the window is inclusive |
4. Custom Window Aggregation
▶ Example
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.
: Custom Aggregation Functions (Difficulty ⭐⭐⭐)
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
'value': np.random.randint(10, 100, 20)
})
# Custom: range (max - min) in window
def window_range(arr):
return arr.max() - arr.min()
df['rolling_range'] = df['value'].rolling(5).apply(window_range, raw=True)
# Custom: coefficient of variation (std/mean)
def cv(arr):
return arr.std() / arr.mean() if arr.mean() != 0 else 0
df['rolling_cv'] = df['value'].rolling(5).apply(cv, raw=True)
# Using rolling.apply with raw=True for performance
# raw=True: receives numpy array (fast)
# raw=False: receives Series (slow but flexible)
print(df.head(10))
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.
5. expanding In Depth
▶ Example
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.
: Custom expanding Functions (Difficulty ⭐)
import pandas as pd
import numpy as np
np.random.seed(42)
df = pd.DataFrame({
'score': np.random.randint(60, 100, 15)
})
# Built-in expanding methods
df['cummean'] = df['score'].expanding().mean()
df['cumstd'] = df['score'].expanding().std()
df['cummax'] = df['score'].expanding().max()
# Expanding with min_periods
df['cummean_5'] = df['score'].expanding(min_periods=5).mean()
# Custom: expanding z-score
def zscore(arr):
if len(arr) < 2: return np.nan
return (arr[-1] - arr.mean()) / arr.std()
df['zscore'] = df['score'].expanding().apply(zscore, raw=True)
print(df.round(2).head(10))
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.
6. ewm Parameters Explained
(1) The Four Parameter Relationships
| Parameter | Relationship | Meaning |
|---|---|---|
| span | com = span - 1 | Similar to an N-period SMA |
| com | α = 1/(1+com) | Center of mass |
| halflife | α = 1 - 0.5^(1/halflife) | Number of periods for weight to halve |
| alpha | Specified directly | Smoothing factor 0<α≤1 |
▶ Example
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.
: ewm Parameter Comparison (Difficulty ⭐⭐)
import pandas as pd
import numpy as np
np.random.seed(42)
s = pd.Series(np.random.randn(30).cumsum() + 100)
# These are all equivalent (approximately)
ewm_span5 = s.ewm(span=5).mean()
ewm_com4 = s.ewm(com=4).mean() # com = span - 1
ewm_hl3 = s.ewm(halflife=3).mean() # different weighting
ewm_alpha = s.ewm(alpha=0.333).mean() # alpha = 2/(span+1)
# Compare with SMA
sma5 = s.rolling(5).mean()
print("SMA vs EWM (first 10):")
print(pd.DataFrame({
'value': s, 'SMA_5': sma5, 'EWM_span5': ewm_span5
}).head(10).round(2))
# ewm std (volatility)
ewm_std = s.ewm(span=10).std()
print(f"\nLatest EWM std: {ewm_std.iloc[-1]:.2f}")
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.
7. groupby + rolling
▶ Example
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.
: Rolling Windows Within Groups (Difficulty ⭐⭐⭐)
import pandas as pd
import numpy as np
# Sensor data: multiple sensors, each with its own time series
np.random.seed(42)
df = pd.DataFrame({
'sensor_id': np.repeat(['T01', 'T02', 'T03'], 20),
'timestamp': list(pd.date_range('2024-01', periods=20, freq='6h')) * 3,
'temperature': np.random.normal(20, 3, 60).round(1)
})
# Rolling within each sensor group
df = df.sort_values(['sensor_id', 'timestamp'])
df['temp_ma'] = df.groupby('sensor_id')['temperature'].transform(
lambda x: x.rolling(4, min_periods=1).mean()
)
# Alternative: groupby + rolling (returns MultiIndex)
result = df.groupby('sensor_id')['temperature'].rolling(4).mean()
print(result.head(12))
# Reset for clean output
df['temp_ma2'] = df.groupby('sensor_id')['temperature'].transform(
lambda x: x.rolling(4).mean().values
)
print(df[df['sensor_id'] == 'T01'].head(6))
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.
8. Complete Example: Exercise Heart Rate Data Analysis
(5) ▶ Comparing Three Window Types
graph LR
A[Window Functions] --> B[rolling Fixed Window]
A --> C[expanding Cumulative Window]
A --> D[ewm Exponential Weighting]
B --> E[Local Trends / Moving Average]
C --> F[Cumulative Stats / From Start to Now]
D --> G[Recent Data Weighted More / Smoothing]
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.
▶ Example
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.
: Full Window Analysis Pipeline (Difficulty ⭐⭐⭐)
import pandas as pd
import numpy as np
# ============================================
# Comprehensive example: Fitness heart rate
# window analysis pipeline
# ============================================
# 1. Generate 2-hour workout data (1 reading per 30s = 240 rows)
np.random.seed(42)
timestamps = pd.date_range('2024-01-15 08:00', periods=240, freq='30s')
exercise_type = (['Warmup'] * 30 + ['Cardio'] * 90 + ['Weights'] * 60 + ['Cooldown'] * 60)
hr_base = ([70] * 30 + list(np.linspace(70, 140, 90)) + [120] * 60 + list(np.linspace(120, 70, 60)))
heart_rates = [max(50, h + np.random.randn() * 5) for h in hr_base]
df = pd.DataFrame({
'timestamp': timestamps,
'exercise': exercise_type,
'heart_rate': np.round(heart_rates, 0)
})
# 2. Rolling: 1-minute (2 readings) and 5-minute (10 readings) moving avg
df['hr_ma_1min'] = df['heart_rate'].rolling(2).mean()
df['hr_ma_5min'] = df['heart_rate'].rolling(10).mean()
# 3. Rolling: heart rate variability (std in 5-min window)
df['hrv'] = df['heart_rate'].rolling(10).std()
# 4. Expanding: cumulative average
df['cum_avg_hr'] = df['heart_rate'].expanding().mean()
# 5. EWM: smoothed trend
df['hr_ewm'] = df['heart_rate'].ewm(span=10).mean()
# 6. Groupby + rolling: per exercise type
df['hr_ma_per_exercise'] = df.groupby('exercise')['heart_rate'].transform(
lambda x: x.rolling(5, min_periods=1).mean()
)
# 7. Summary by exercise
print("=== Exercise Summary ===")
summary = df.groupby('exercise')['heart_rate'].agg(['mean', 'max', 'min', 'std']).round(1)
print(summary)
> **Output:** Run in your local Python environment (pandas 2.x). The Piston server does not have pandas pre-installed. Please install it locally (`pip install pandas`) and follow along. Actual values may vary slightly depending on the pandas version.
❓ FAQ
rolling('7D') uses a 7-day calendar window, while rolling(7) uses a 7-row window. Time-based windows are ideal for non-uniformly sampled data (skipping weekends or missing days), while row-based windows suit uniform sampling. Time-based windows require the Index to be a DatetimeIndex, or you can specify a time column with the on parameter.pip install scipy. win_type applies weights to data within the window (higher weight in the middle, lower at the edges), producing smoother results than an equal-weight window.📖 Summary
- Advanced rolling: min_periods allows partial windows, center centers the window, win_type applies weighting, on specifies a time column
- Custom aggregation uses .apply(func, raw=True); raw=True is about 10x faster
- expanding accumulates from the beginning to the current position and supports any aggregation function
- ewm parameters: span (N periods) / com (N-1) / halflife / alpha — all four are equivalent
- groupby+rolling: sort first, then compute; transform returns a same-length result
- Choosing a window type: local trends → rolling, cumulative → expanding, smoothing → ewm
📝 Exercises
- Basic (Difficulty ⭐): Create 30 days of temperature data. Use rolling(7, min_periods=3) to compute the moving mean and standard deviation, and use ewm(span=7) to compute exponential smoothing.
- Intermediate (Difficulty ⭐⭐): Create multi-sensor temperature data (3 sensors, 20 rows each). Use groupby+rolling to compute a 5-period moving average for each sensor.
- Challenge (Difficulty ⭐⭐⭐): Simulate 240 rows of exercise heart rate data (with 4 exercise types). Complete the following pipeline: rolling multi-metric (mean/std/max) → expanding cumulative average → ewm smoothing → groupby+rolling by exercise type → summary report.
← Previous Lesson: MultiIndex · Next Lesson: Visualization →