pd.merge_asof(left, right, on="time")
mergeAsof is an ordered left-join that matches on the nearest key
rather than an exact key. It is especially useful for time-series data — e.g., matching
each trade to the most recent quote.
mergeAsof.direction: controls whether to look backward (default), forward, or for the nearest key.by: require additional columns to match exactly before doing the asof lookup (e.g. by ticker).tolerance: ignore matches further than this numeric distance.import { DataFrame, mergeAsof } from "tsb";
// Each trade is matched to the most recent quote (backward asof)
const trades = DataFrame.fromColumns({
time: [1, 5, 10],
price: [100, 200, 300],
});
const quotes = DataFrame.fromColumns({
time: [2, 6],
bid: [98, 195],
});
const result = mergeAsof(trades, quotes, { on: "time" });
// time | price | bid
// 1 | 100 | null ← no quote ≤ 1
// 5 | 200 | 98 ← most recent quote ≤ 5 is at time=2
// 10 | 300 | 195 ← most recent quote ≤ 10 is at time=6
// Match each event to the next scheduled announcement
const events = DataFrame.fromColumns({ t: [1, 3, 7], v: [10, 30, 70] });
const schedule = DataFrame.fromColumns({ t: [2, 6, 10], w: [20, 60, 100] });
const result = mergeAsof(events, schedule, {
on: "t",
direction: "forward",
});
// t=1 → t=2 (w=20), t=3 → t=6 (w=60), t=7 → t=10 (w=100)
const result = mergeAsof(trades, quotes, {
on: "time",
direction: "nearest",
});
// Picks the quote with the smallest absolute time difference.
by// Match trades to quotes within the same ticker symbol
const trades = DataFrame.fromColumns({
time: [1, 2, 3, 4],
ticker: ["AAPL","MSFT","AAPL","MSFT"],
price: [100, 200, 110, 210],
});
const quotes = DataFrame.fromColumns({
time: [1, 1, 3, 3],
ticker: ["AAPL","MSFT","AAPL","MSFT"],
bid: [99, 198, 109, 208],
});
mergeAsof(trades, quotes, { on: "time", by: "ticker" });
// Only match if the key distance is ≤ 2
mergeAsof(left, right, { on: "t", tolerance: 2 });
mergeAsof(left, right, {
left_on: "trade_time",
right_on: "quote_time",
});
mergeAsof(left, right, {
left_index: true,
right_on: "timestamp",
});
| Option | Default | Description |
|---|---|---|
on | — | Shared key column name |
left_on / right_on | — | Different key columns per side |
left_index / right_index | false | Use index as key |
by | — | Column(s) that must match exactly |
left_by / right_by | — | Different by-columns per side |
direction | "backward" | "backward", "forward", or "nearest" |
tolerance | null | Max numeric key distance for a match |
allow_exact_matches | true | Include exact key matches |
suffixes | ["_x","_y"] | Suffixes for overlapping column names |