pandas: groupby, joins, resampling and time zones
PY · Chapter 312 min readAsked at Two Sigma, Citadel Securities, Point72, QuantCo
After this lesson you should be able to
- Use groupby–transform to work within groups without losing the shape.
- Choose the right join, including
merge_asof. - Handle time zones and resampling without silently shifting the data.
pandas is where research code actually lives, and the operations that matter are the ones that respect grouping and time. Nearly every subtle bug in a research pipeline is a join that dropped rows, a groupby that reduced when it should have transformed, or a timestamp in the wrong zone.
| Verb | Returns | Use for |
|---|---|---|
agg | One row per group | Summaries — mean return by sector |
transform | The original shape | Within-group standardisation |
apply | Whatever you return | Flexible and slow; a last resort |
filter | A subset of rows | Keeping groups meeting a condition |
transform is the one that matters most in signal work: cross-sectional z-scoring is df.groupby("date")["signal"].transform(lambda s: (s - s.mean()) / s.std()), and it returns something you can assign straight back.merge and merge_asof sort and sweep; a loop that looks up each row in a DataFrame by value does the other one, which is why a join written as a loop appears to hang rather than to be slow.| Join | Keeps | Watch for |
|---|---|---|
| inner | Only matching keys | Silent row loss |
| left | All of the left | NaNs where the right had no match |
| outer | Everything | Row explosion on duplicate keys |
merge_asof | Nearest earlier key | Both sides must be sorted |
Why merge_asof is the market-data join. Market data arrives on irregular timestamps, so an exact-match join finds almost nothing and a nearest-match join can reach forward in time. merge_asof with the default backward direction matches each row to the most recent row on the other side at or before it — which is precisely "what did I know at this instant". The tolerance argument then caps how stale a match may be, so a quote from an hour ago does not get attached to a trade. It is the one join whose semantics are built for causality rather than for set membership.
Example 3.4
You join a daily signal onto minute-bar prices and the row count explodes from 100,000 to 39 million. What happened?
Show the worked solutionHide the worked solution
Worked solution
- Formula
- Substitute
- Solve
- Answer
Sanity check. That may even be what you wanted — broadcasting a daily signal onto every minute is a legitimate operation. What is not acceptable is being surprised by it, which is why checking the row count against the expected multiple is a standard step.
The rest of this lesson is in Premium
You have read the opening. 10 more sections follow, including 3 worked examples and 3 quick checks.
Nothing is charged for 7 days, and you can cancel before then. Or read Complexity: reading it off, and deriving it in full, free.