0Pricing
Pandas & NumPy Academy · Lesson

Resampling Time Series

Downsample daily data to monthly with resample('ME').sum() and upsample with forward fill to fill in missing intervals.

Resampling Time Series is a free Pandas & NumPy Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Pandas & NumPy Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Is Resampling?

Resampling changes the frequency of a time series. Downsampling reduces frequency — for example, converting daily data to monthly totals. Upsampling increases frequency — for example, converting monthly data to daily, filling gaps with interpolated values. Both operations require a DatetimeIndex and are performed with the resample() method, which is Pandas' time-aware version of groupby().

The resample() Method

df.resample(rule) creates a DatetimeIndexResampler object, where rule is a frequency offset alias. Like groupby(), nothing is computed until you chain an aggregation method. Common rules: 'D' (day), 'W' (week), 'ME' (month end), 'QE' (quarter end), 'YE' (year end). The DataFrame must have a DatetimeIndex.

import pandas as pd
import numpy as np

np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=90, freq='D')
df = pd.DataFrame({'sales': np.random.randint(100, 500, 90)}, index=dates)

print(df.head())
print('Shape:', df.shape)  # (90, 1) -- 90 daily rows

Downsampling: Daily to Monthly

To downsample daily data to monthly, call resample('ME') and chain an aggregation. sum() gives monthly totals; mean() gives monthly averages; last() gives the last value in each period. The result has one row per period, with the period-end date as the index label.

# Monthly totals
monthly_sum = df.resample('ME').sum()
print(monthly_sum)
# sales
# 2024-01-31    9876
# 2024-02-29    8765
# 2024-03-31    9234

# Monthly averages
monthly_mean = df.resample('ME').mean().round(1)
print(monthly_mean)

Downsampling to Weekly

Resample with 'W' to aggregate by calendar week ending on Sunday. Use 'W-MON' if you want weeks ending on Monday. Pandas groups all dates within each week together and applies the aggregation. This is useful for weekly reporting where you want one summary row per week.

# Weekly sum
weekly = df.resample('W').sum()
print(weekly.head())
# sales
# 2024-01-07    2010  <- Jan 1-7
# 2024-01-14    2340  <- Jan 8-14
# ...

# Weekly max and count using agg()
weekly_stats = df.resample('W').agg(['sum', 'mean', 'max'])
print(weekly_stats.head())

Applying Multiple Aggregations

Just like groupby(), you can chain .agg() on a resampler to compute multiple statistics in a single pass. Pass a list of function names or a dict for named aggregations. This is the most efficient way to build a comprehensive time-series summary table.

monthly_stats = df.resample('ME').agg(
    total_sales=('sales', 'sum'),
    avg_sales=('sales', 'mean'),
    peak_sales=('sales', 'max'),
    days_counted=('sales', 'count')
)
print(monthly_stats.round(1))
#             total_sales  avg_sales  peak_sales  days_counted
# 2024-01-31         9876      318.6         499            31

OHLC Resampling for Financial Data

For financial time series, the .ohlc() method resamples to Open, High, Low, Close (OHLC) values per period — the standard candlestick representation. This is only meaningful for numeric data representing a price or level. Each column in the input gets four OHLC columns in the output.

# OHLC monthly resampling
ohlc = df['sales'].resample('ME').ohlc()
print(ohlc)
#             open  high   low  close
# 2024-01-31   365   499   101    243
# 2024-02-29   ...

Upsampling: Monthly to Daily

Upsampling increases frequency, which creates rows for timestamps that did not exist in the original data. These new rows initially contain NaN. You then fill them with a method that makes sense for your data: ffill() (forward fill — carry the last known value forward) or bfill() (backward fill — use the next known value), or interpolate() for smooth interpolation.

# Monthly data
monthly = pd.Series(
    [100, 120, 115],
    index=pd.date_range('2024-01-31', periods=3, freq='ME')
)
print(monthly)

# Upsample to daily (creates NaN rows)
daily = monthly.resample('D').asfreq()
print(daily.head(10))
# 2024-01-31    100.0
# 2024-02-01      NaN  <- new row
# ...

Filling Upsampled Gaps

After upsampling, fill the NaN gaps created for new timestamps. ffill() propagates the last known value forward until the next real observation — good for prices where the last traded price remains valid. interpolate(method='linear') fills with linearly spaced values between observations — good for smooth continuous variables.

# Forward fill: carry monthly value forward to every day
daily_ffill = monthly.resample('D').ffill()
print(daily_ffill.head(35))
# 2024-01-31    100
# 2024-02-01    100  <- forward filled
# ...
# 2024-02-29    120  <- new month value

# Linear interpolation
daily_interp = monthly.resample('D').interpolate(method='linear')
print(daily_interp.head(10))

Resampling Within Groups

You can combine groupby() and resample() to resample within each group separately. Call groupby(column).resample(rule) on a DataFrame with a DatetimeIndex. This produces a result with a MultiIndex where the outer level is the group key and the inner level is the resampled time period — very useful for product-level or region-level time series analysis.

df2 = pd.DataFrame({
    'product': ['A', 'B', 'A', 'B', 'A', 'B'],
    'sales': [100, 150, 120, 130, 110, 160]
}, index=pd.date_range('2024-01-01', periods=6, freq='ME'))

# Monthly sum per product
result = df2.groupby('product').resample('QE').sum()
print(result)

Custom Offset Anchoring

By default, 'ME' anchors to calendar month-end dates. For non-standard fiscal periods, use offsets like 'QS-APR' (quarter starting April) or 'YS-OCT' (year starting October). Pandas supports many anchored offset aliases. Check the Pandas documentation for the full list when working with non-calendar fiscal periods.

# Fiscal year starting in April
fiscal_annual = df.resample('YE-MAR').sum()
print(fiscal_annual)
# sales
# 2024-03-31    XXXX  <- April 2023 to March 2024

# Quarter starting in April
fiscal_q = df.resample('QE-MAR').mean().round(0)
print(fiscal_q)

Checking Resampled Result Integrity

After resampling, always verify the result makes sense. Check that the number of output periods matches expectations, that no periods are missing (look for NaN in the output), and that the sum of monthly totals equals the sum of the original daily data when downsampling with sum(). These sanity checks catch off-by-one errors in frequency strings and missing date ranges.

monthly = df.resample('ME').sum()

# Verify: monthly totals should sum to daily total
assert monthly['sales'].sum() == df['sales'].sum(), 'Sum mismatch!'

# Verify: expected number of months
print('Months:', len(monthly))  # should be 3 for 90 days Jan-Mar

# Check for any NaN
print('NaN count:', monthly.isna().sum().sum())

Quick Check

Test your understanding of resampling time series from this lesson.

Lesson Recap

In this lesson you learned: resample() is the time-aware equivalent of groupby() for changing time series frequency; downsampling aggregates data (daily to monthly with sum/mean); upsampling creates new timestamps that must be filled with ffill() or interpolate(); and you can combine groupby() with resample() for group-level time aggregation. Next up we explore shifting and lag features.

Frequently asked questions

Is the “Resampling Time Series” lesson free?

Yes — the full text of “Resampling Time Series” is free to read here on the web, and the Pandas & NumPy Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Pandas & NumPy Academy course, upgrade to CoddyKit PRO.

What will I learn in “Resampling Time Series”?

Downsample daily data to monthly with resample('ME').sum() and upsample with forward fill to fill in missing intervals. You practise Pandas & NumPy Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Pandas & NumPy Academy?

No prior experience is required. Pandas & NumPy Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Resampling Time Series” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Pandas & NumPy Academy lesson?

Yes. Every Pandas & NumPy Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. DatetimeIndex and Period Ranges
  2. Resampling Time Series
  3. Shifting and Lag Features
  4. Extracting Temporal Features
← Back to Pandas & NumPy Academy