merge_asof 默认实现最近匹配但不支持 tolerance 参数,需先 merge_asof 再用 query 筛选满足容差的行;左右 on 列须预排序,backward 方向用 delta = left_on - right_on 并 query('delta >= 0 and delta
merge_asof 的设计目标就是在左表每行的 on 列值附近,找右表中「最大但不超过它」(direction='backward')或「最小但不低于它」(direction='forward')的行——这本身就是一种“最近匹配”,但它**不接受 tolerance 范围限制**。如果你看到报错 TypeError: merge_asof() got an unexpected keyword argument 'tolerance',说明你误以为它像 numpy.isclose 那样支持容差。
要实现带 tolerance 的最近匹配,必须手动过滤 + 二次筛选,不能靠 merge_asof 一步到位。
核心思路:先用 merge_asof 找到每个左行最接近的右行(默认 direction='backward'),再计算实际距离,用 query 剔除超出 tolerance 的结果。
on 列已排序(merge_asof 强制要求)delta = left_on - right_on(对 backward 场景).query('delta = 0') 筛选(注意符号方向)abs(left_on - right_on) ,但此时 merge_asof 的单向语义失效,应改用 pd.merge + sort_values + drop_duplicates 组合
left = pd.DataFrame({'time': [1, 5, 10], 'val': ['a', 'b', 'c']})
right = pd.DataFrame({'time': [2, 4, 8, 12], 'info': ['x', 'y', 'z', 'w']})
tolerance = 2
result = pd.merge_asof(left.sort_values('time'),
right.sort_values('time'),
on='time',
direction='

backward')
result['delta'] = result['time'] - result['time']实际应为 result['delta'] = result['time'] - result['time_right'](需重命名右表 time)
result = result.query('delta <= @tolerance')
当 tolerance 占数据跨度比例较高(例如时间范围 0–100,tolerance=30),merge_asof 的“单侧最近”逻辑会漏掉本应在容差内、但方向相反的候选行。这时更稳妥的做法是:对左表每行,在右表中搜索所有满足 abs(left_time - right_time) 的行,再取绝对差最小的那个。
pd.merge 做笛卡尔积(仅适用于中小数据量)scipy.spatial.cKDTree 加速(大数据量、单维度)right.loc[(right.time - row.time).abs().idxmin()],但性能差;可向量化为 right.iloc[abs(right.time.values - left.time.values[:, None]).argmin(axis=1)]
keep='first' 或显式去重merge_asof 要求左右表 on 列严格单调递增,如果存在重复值,它会取最后一个匹配项(不是报错)。但 tolerance 过滤后,可能只剩空结果——这不是 bug,而是数据本身不满足条件。
left['on'].is_monotonic_increasing 和 right['on'].is_monotonic_increasing
on 值,merge_asof 会任选其一(按原始顺序),无法控制;如需确定行为,先 right.drop_duplicates(subset=['on'], keep='first')
pd.Timedelta(如 pd.Timedelta('2s')),不可直接用整数秒真正难的不是写哪一行代码,而是想清楚:你要的“最近”,是指时间上最邻近的一条记录,还是在某个误差范围内、且满足业务逻辑(比如不能用未来的数据)的最优解。这两者在建模阶段就该区分清楚。