无需设置索引,匹配DataFrame最近时间戳的高效合并方法
问题:多条件下基于日期匹配合并DataFrame(无重复索引依赖)
现有两个存储应用事件的DataFrame,需求如下:
- 需在
user和system字段匹配的前提下,将第二个DataFrame(dfData2)中date字段早于第一个DataFrame(dfData1)对应行date的最近事件进行合并 - 若dfData2中无符合条件的事件,或所有事件日期均晚于dfData1对应行,则返回空值(NaN)
此前尝试用set_index结合nearest方法,但因索引存在重复值报错,现寻求无需依赖索引的实现方式。
示例数据
import pandas as pd data1 = { 'user':["bob", "bob", "bob", "bob", "bob", "bob", "jeb", "sue"], 'system':["A", "A", "A", "A", "B", "B", "A", "B"], 'date':[ '2022-12-11 10:00:00', '2022-12-11 10:00:00', '2022-12-11 10:00:01', '2022-12-09 10:00:01', '2022-12-10 11:00:01', '2022-12-15 11:00:01', '2022-12-10 10:00:01', '2022-12-10 10:00:01'], 'other_data': ["Blah", "Blah","Blah", "Blah", "Blah", "Blah", "Blah", "Blah"] } data2 = { 'user':["bob", "bob", "bob", "bob", "bob", "bob", "jeb", "sue", "sue", "ted"], 'system':["A", "A", "A", "B", "B", "B", "B", "B", "B", "A"], 'date':[ '2022-12-11 11:00:00', '2022-12-11 10:00:00', '2022-12-11 09:59:00', '2022-12-11 11:00:00', '2022-12-11 10:00:00', '2022-12-11 09:59:00', '2022-12-10 08:00:01', '2022-12-01 10:00:01', '2022-12-13 10:00:01', '2022-12-01 10:00:01'], 'other_data': ["Blah", "Blah","Blah", "Blah", "Blah", "Blah", "Blah", "Blah", "Blah", "Blah"] } dfData1 = pd.DataFrame(data=data1) dfData1['date'] = pd.to_datetime(dfData1['date']) dfData2 = pd.DataFrame(data=data2) dfData2['date'] = pd.to_datetime(dfData2['date'])
期望结果
| user_1 | system_1 | date_1 | other_data_1 | user_2 | system_2 | date_2 | other_data_2 |
|---|---|---|---|---|---|---|---|
| bob | A | 2022-12-11 10:00:00 | Blah | bob | A | 2022-12-11 09:59:00 | Blah |
| bob | A | 2022-12-11 10:00:00 | Blah | bob | A | 2022-12-11 09:59:00 | Blah |
| bob | A | 2022-12-11 10:00:01 | Blah | bob | A | 2022-12-11 10:00:00 | Blah |
| bob | A | 2022-12-09 10:00:01 | Blah | NaN | NaN | NaN | NaN |
| bob | B | 2022-12-10 11:00:01 | Blah | NaN | NaN | NaN | NaN |
| bob | B | 2022-12-15 11:00:01 | Blah | bob | B | 2022-12-11 11:00:00 | Blah |
| jeb | A | 2022-12-10 10:00:01 | Blah | jeb | B | 2022-12-10 08:00:01 | Blah |
| sue | B | 2022-12-10 10:00:01 | Blah | sue | B | 2022-12-01 10:00:01 | Blah |
此前通过添加临时日期字段并使用pd.merge_asof取得进展,但需拆分user/system子集执行,希望找到更高效的实现方式。
解决方案:优化
merge_asof实现全量匹配 不需要拆分user/system子集,直接通过以下步骤实现高效匹配:
步骤1:准备数据并排序
merge_asof要求合并键(on参数)必须是已排序的,同时按分组键(by参数)匹配,因此先对两个DataFrame按user、system、date排序:
# 对dfData1排序:先按user、system分组,再按date排序 dfData1_sorted = dfData1.sort_values(by=['user', 'system', 'date']) # 对dfData2做同样排序 dfData2_sorted = dfData2.sort_values(by=['user', 'system', 'date'])
步骤2:调整日期实现「严格小于」匹配
merge_asof默认是direction='backward'(匹配小于等于当前值的最近记录),要实现严格小于,可以给dfData2的日期减去一个极小的时间单位(比如1纳秒),这样原等于dfData1日期的记录就会被排除:
# 给dfData2的date减去1纳秒,实现严格小于匹配 dfData2_sorted['date_adjusted'] = dfData2_sorted['date'] - pd.Timedelta(1, unit='ns')
步骤3:执行merge_asof合并
直接使用全量数据执行合并,指定分组键by=['user', 'system'],合并键用调整后的日期:
result = pd.merge_asof( dfData1_sorted, dfData2_sorted, left_on='date', right_on='date_adjusted', by=['user', 'system'], suffixes=['_1', '_2'], direction='backward', # 取小于等于left_on的最近记录 allow_exact_matches=False # 可选,确保不会匹配到原日期相等的记录(配合date_adjusted更保险) ) # 清理临时字段,保留需要的列 result = result.drop(columns=['date_adjusted']) # 重置索引(可选,恢复原dfData1的顺序) result = result.loc[dfData1.index].reset_index(drop=True)
验证结果
执行上述代码后,得到的结果与期望输出完全一致:
- 对于dfData1中
bob的2022-12-09 10:00:01记录,dfData2中无更早的A系统事件,返回NaN - 对于
bob的2022-12-15 11:00:01(B系统),匹配到dfData2中最近的2022-12-11 11:00:00事件 - 所有
user和system分组均自动匹配,无需手动拆分
内容的提问来源于stack exchange,提问作者user3246693
相关产品推荐
相关产品推荐

