按业务规则合并Pandas DataFrame:ID匹配且日期按顺序关联
解决Pandas DataFrame按ID匹配且日期一对一关联的问题
要实现每个Date_A和Date_B仅匹配一次,且Date_B关联到下一个最早的Date_A之后的需求,不能直接用普通的pd.merge()(它会产生笛卡尔积导致重复匹配),可以按以下步骤实现:
步骤1:转换日期格式并排序
先把字符串格式的日期转换成datetime类型方便后续比较,同时对两个DataFrame按ID和日期排序,确保顺序符合时间逻辑。
import pandas as pd # 构造示例数据 df_a = pd.DataFrame({ 'ID': [1,1,1,2,3,3], 'Date_A': ['01012023','02012023','03012023','02152023','02012023','04012023'] }) df_b = pd.DataFrame({ 'ID': [1,1,3], 'Date_B': ['01112023','03112023','05012023'] }) # 转换日期字符串为datetime类型 df_a['Date_A'] = pd.to_datetime(df_a['Date_A'], format='%m%d%Y') df_b['Date_B'] = pd.to_datetime(df_b['Date_B'], format='%m%d%Y') # 按ID和日期排序 df_a = df_a.sort_values(['ID', 'Date_A']).reset_index(drop=True) df_b = df_b.sort_values(['ID', 'Date_B']).reset_index(drop=True)
步骤2:自定义分组匹配函数
对每个ID分组,遍历该ID下的Date_A,为每个Date_A匹配第一个未被使用的、晚于它的Date_B,确保每条记录仅被使用一次。
def match_dates(group_a, group_b): used_b_indices = set() # 遍历当前ID下的每个Date_A for idx_a, row_a in group_a.iterrows(): # 筛选未被使用且晚于当前Date_A的Date_B available_b = group_b[~group_b.index.isin(used_b_indices) & (group_b['Date_B'] > row_a['Date_A'])] if not available_b.empty: # 取最早的符合条件的Date_B matched_b = available_b.iloc[0] group_a.loc[idx_a, 'Date_B'] = matched_b['Date_B'] used_b_indices.add(matched_b.name) else: group_a.loc[idx_a, 'Date_B'] = pd.NaT return group_a # 按ID分组处理匹配逻辑 result = df_a.groupby('ID', group_keys=False).apply( lambda x: match_dates(x, df_b[df_b['ID'] == x.name]) ) # 转换回原日期字符串格式(可选) result['Date_A'] = result['Date_A'].dt.strftime('%m%d%Y') result['Date_B'] = result['Date_B'].dt.strftime('%m%d%Y').fillna('') print(result)
最终输出结果
运行上述代码后,得到的结果与你期望的一致:
ID Date_A Date_B 0 1 01012023 01112023 1 1 02012023 03112023 2 1 03012023 3 2 02152023 4 3 02012023 05012023 5 3 04012023
内容的提问来源于stack exchange,提问作者325
相关产品推荐
相关产品推荐

