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 windowsjoin_asofmatches the nearest earlier (or later) row by time- Sort by the time column first; these operations require it
Watch out for
- Running
group_by_dynamicorjoin_asofon unsorted timestamps.