
本文介绍使用 `pandas.merge_asof()` 高效实现跨 dataframe 的时间区间匹配,避免低效的嵌套循环,在百万级数据下仍保持秒级响应。
在数据分析中,常需将一个事件表(如设备操作日志)与一个时段表(如工单生效区间)按主键+时间范围关联,并将时段表中的字段(如 VALUE)作为新列注入事件表。若采用双重 for 循环逐行比对(如 df1['EVENT_TIME'] 是否落在 df2['IN_TIME'] 与 df2['OUT_TIME'] 之间),时间复杂度为 O(n×m),面对 10 万+ 行数据时极易超时(如原问题中耗时 >10 分钟)。幸运的是,Pandas 提供了专为此类场景优化的 merge_asof() 方法——它基于排序后的一对多最近匹配机制,配合后续逻辑过滤,可将性能提升数十倍。
✅ 正确做法:merge_asof + 时间范围校验
核心思路分三步:
- 统一时间类型:确保 EVENT_TIME、IN_TIME、OUT_TIME 均为 datetime64 类型;
- merge_asof 初步关联:按 LOT 分组,以 EVENT_TIME(左)和 IN_TIME(右)为键进行“向后最近匹配”(默认策略),快速找到每个事件最接近且不晚于其发生时刻的 IN_TIME 所在记录;
- 二次过滤:用 .assign() 和布尔掩码保留满足 EVENT_TIME ≤ OUT_TIME 的匹配结果,剔除超出时段上限的误匹配。
import pandas as pd
# 示例数据构造
data1 = {
'LOT': ['A', 'A', 'A', 'A', 'A', 'A'],
'SLOT': [1, 2, 3, 4, 5, 6],
'EVENT_TIME': ['2024-01-20 13:30', '2024-01-20 13:36',
'2024-01-21 14:28', '2024-01-21 14:30',
'2024-01-21 14:32', '2024-01-21 14:34']
}
data2 = {
'LOT': ['A', 'A'],
'IN_TIME': ['2024-01-20 13:20', '2024-01-21 14:25'],
'OUT_TIME': ['2024-01-20 13:40', '2024-01-21 14:50'],
'VALUE': [13, 15]
}
df1 = pd.DataFrame(data1)
df2 = pd.DataFrame(data2)
# 步骤1:转为 datetime
df1['EVENT_TIME'] = pd.to_datetime(df1['EVENT_TIME'])
df2['IN_TIME'] = pd.to_datetime(df2['IN_TIME'])
df2['OUT_TIME'] = pd.to_datetime(df2['OUT_TIME'])
# 步骤2+3:merge_asof + 范围校验
result = (
pd.merge_asof(
df1.sort_values('EVENT_TIME'),
df2.sort_values('IN_TIME'),
by='LOT',
left_on='EVENT_TIME',
right_on='IN_TIME',
allow_exact_matches=True, # 允许 EVENT_TIME == IN_TIME
direction='backward' # 取 IN_TIME ≤ EVENT_TIME 的最大值(最接近的起始点)
)
.assign(VALUE=lambda x: x['VALUE'].where(x['EVENT_TIME'] <= x['OUT_TIME']))
.drop(['IN_TIME', 'OUT_TIME'], axis=1)
.rename(columns={'VALUE': 'DATA'})
)
print(result)输出结果与预期完全一致:
LOT SLOT EVENT_TIME DATA 0 A 1 2024-01-20 13:30:00 13.0 1 A 2 2024-01-20 13:36:00 13.0 2 A 3 2024-01-21 14:28:00 15.0 3 A 4 2024-01-21 14:30:00 15.0 4 A 5 2024-01-21 14:32:00 15.0 5 A 6 2024-01-21 14:34:00 15.0
⚠️ 注意事项与最佳实践
- 必须排序:merge_asof 要求左右 DataFrame 均按关联时间列升序排列(sort_values 不可省略);
- direction 参数选择:本例用 'backward'(找 ≤ EVENT_TIME 的最大 IN_TIME),若需找 ≥ EVENT_TIME 的最小 IN_TIME,则用 'forward';'nearest' 则取绝对距离最近者(但需额外验证是否在 [IN, OUT] 内);
- 处理缺失匹配:未匹配到任何时段的行,DATA 将为 NaN,可根据业务需要 .fillna() 或 .dropna();
- 扩展性提示:当 df2 中存在重叠时段或同一 LOT 多个非重叠区间时,该方法依然稳健;若需支持更复杂的多维范围匹配(如地理围栏),建议转向 dask 或数据库 JOIN ... ON a.time BETWEEN b.start AND b.end。
通过 merge_asof 替代暴力循环,不仅代码更简洁、可读性更强,更能将 10 万行数据的匹配耗时从分钟级降至毫秒级,是 Pandas 时间序列关联任务的首选方案。










