Time series: dynamic groups, rolling and asof

Polars · Advanced · 15 min

Metrics, logs and financial data are time series, and these three tools cover most of what analyses need.

`group_by_dynamic` buckets rows into time windows, like pandas resample:

df.sort("ts").group_by_dynamic("ts", every="1h", group_by="sensor").agg(pl.col("v").mean())

every sets the step, period the window length (defaults to every; make it longer for overlapping windows), closed and label decide window edges and labels. Durations are strings like "30m", "1d", "1w", "1mo", "1q". The time column must be sorted.

Rolling windows look back from each row. df.rolling("ts", period="2d").agg(...) computes over the last 2 days at every row, and expressions such as pl.col("v").rolling_mean_by("ts", window_size="2d") do the same inside with_columns. The fixed-count variants, rolling_mean(window_size=3), count rows instead of time.

`join_asof` joins each left row to the nearest right row by a sorted key, typically "the last quote at or before each trade": trades.join_asof(quotes, on="ts", by="ticker", strategy="backward", tolerance="5s"). strategy can be "backward", "forward" or "nearest".

To fill gaps, df.upsample("ts", every="1d") inserts missing timestamps, and fill_null(strategy="forward") or interpolate() fill the values.

Example

import polars as pl
from datetime import datetime as dt

trades = pl.DataFrame({
    "ts": [dt(2026, 1, 1, 9, 0, 5), dt(2026, 1, 1, 9, 0, 40), dt(2026, 1, 1, 10, 30)],
    "qty": [100, 50, 70],
})
quotes = pl.DataFrame({
    "ts": [dt(2026, 1, 1, 9, 0, 0), dt(2026, 1, 1, 9, 0, 30)],
    "bid": [10.0, 10.5],
})

hourly = trades.group_by_dynamic("ts", every="1h").agg(qty=pl.col("qty").sum())
print(hourly.rows())
# [(datetime(2026, 1, 1, 9, 0), 150), (datetime(2026, 1, 1, 10, 0), 70)]

matched = trades.join_asof(quotes, on="ts", strategy="backward", tolerance="1m")
print(matched["bid"].to_list())   # [10.0, 10.5, None]  (last trade: quote too old)

Remember

  • group_by_dynamic = calendar windows (resampling); rolling = look-back windows
  • join_asof matches the nearest earlier (or later) row by time
  • Sort by the time column first; these operations require it

Watch out for

  • Running group_by_dynamic or join_asof on unsorted timestamps.

Check yourself

  1. For each trade you need the most recent quote at or before the trade time. Which tool?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.pola.rs/user-guide/transformations/time-series/rolling/