无法使用pandas.merge_asof实现多列匹配?前向最近日期合并需求
多列分组后按前向最近日期合并Pandas DataFrame
需要合并两个包含a、b列和date日期列的DataFrame,要求先按a、b列匹配分组,再为df1的每条记录找到df2中不早于当前日期的最近日期(前向匹配)。
原始数据
df1
a b date 100 200 2022-01-03 100 200 2022-01-04 101 200 2022-01-05 101 200 2022-01-06 101 200 2022-01-07
df2
a b date 100 200 2022-01-04 100 200 2022-01-06 101 200 2022-01-03 101 200 2022-01-06 101 200 2022-01-09
预期输出
a b date_x date_y 100 200 2022-01-03 2022-01-04 100 200 2022-01-04 2022-01-04 101 200 2022-01-05 2022-01-06 # 不选2022-01-03,因为早于当前日期 101 200 2022-01-06 2022-01-06 101 200 2022-01-07 2022-01-09
解决方案
方法1:使用merge_asof(推荐,效率更高)
pandas.merge_asof支持通过by参数指定多列分组匹配,结合direction='forward'实现前向最近日期匹配。注意必须先对两个DataFrame按分组列和日期列排序。
完整代码:
import pandas as pd # 构造原始数据 df1 = pd.DataFrame({ 'a': [100, 100, 101, 101, 101], 'b': [200, 200, 200, 200, 200], 'date': ['2022-01-03', '2022-01-04', '2022-01-05', '2022-01-06', '2022-01-07'] }) df2 = pd.DataFrame({ 'a': [100, 100, 101, 101, 101], 'b': [200, 200, 200, 200, 200], 'date': ['2022-01-04', '2022-01-06', '2022-01-03', '2022-01-06', '2022-01-09'] }) # 转换日期格式为datetime df1['date'] = pd.to_datetime(df1['date']) df2['date'] = pd.to_datetime(df2['date']) # 按分组列(a、b)和日期列排序,merge_asof要求输入必须有序 df1_sorted = df1.sort_values(by=['a', 'b', 'date']) df2_sorted = df2.sort_values(by=['a', 'b', 'date']) # 执行前向合并 result = pd.merge_asof( df1_sorted, df2_sorted, by=['a', 'b'], # 按a、b分组匹配 on='date', # 按date列执行前向匹配 direction='forward', # 找第一个>=当前日期的记录 suffixes=('_x', '_y') # 区分两个df的日期列 ) # 重置索引(可选,恢复原始顺序) result = result.sort_index() print(result)
输出结果与预期完全一致。
方法2:分组后使用searchsorted(兼容旧版本Pandas)
如果你的Pandas版本不支持多列by参数(较旧版本),可以通过分组+searchsorted实现:
import pandas as pd # 构造并转换数据(同方法1) df1['date'] = pd.to_datetime(df1['date']) df2['date'] = pd.to_datetime(df2['date']) # 对df2按a、b分组,预存每个分组的排序后日期列表 df2_grouped = df2.groupby(['a', 'b'])['date'].apply(sorted).to_dict() # 定义函数:为每条df1记录找到对应分组的前向最近日期 def get_closest_forward_date(row): date_list = df2_grouped.get((row['a'], row['b']), []) if not date_list: return pd.NaT # 找到第一个>=当前日期的索引位置 idx = pd.Series(date_list).searchsorted(row['date'], side='left') return date_list[idx] if idx < len(date_list) else pd.NaT # 应用函数生成date_y列 df1['date_y'] = df1.apply(get_closest_forward_date, axis=1) df1.rename(columns={'date': 'date_x'}, inplace=True) print(df1)
内容的提问来源于stack exchange,提问作者sakalansaka
相关产品推荐
相关产品推荐

