如何匹配ID并筛选两DataFrame中180天内的日期,生成最接近组合
解决方法:匹配同ID且日期差180天内的最接近日期组合
我来帮你搞定这个需求,用Pandas的核心操作就能轻松实现,步骤清晰还容易复用,下面是完整的实现流程和代码:
步骤1:准备示例数据并转换日期类型
首先我们先把示例里的两个DataFrame定义出来,并且一定要把日期列转换成datetime类型——这是计算日期差的基础:
import pandas as pd # 示例DataFrame A df_a = pd.DataFrame({ 'ID': [42, 42, 167, 167, 105, 105], 'Date_A': ['2012-07-21', '2013-04-12', '2009-04-27', '2010-04-19', '2010-12-16', '2012-01-05'] }) # 示例DataFrame B df_b = pd.DataFrame({ 'ID': [12, 35, 42, 42, 167, 105, 105, 105, 105, 105], 'Date_B': ['2016-09-08', '2008-02-02', '2012-01-09', '2013-03-13', '2010-08-02', '2010-11-26', '2011-08-12', '2011-11-11', '2013-03-15', '2013-09-13'] }) # 将日期列转换为datetime类型,否则无法计算日期差 df_a['Date_A'] = pd.to_datetime(df_a['Date_A']) df_b['Date_B'] = pd.to_datetime(df_b['Date_B'])
步骤2:合并DataFrame并计算日期差
我们按ID把两个DataFrame合并,这样所有同ID的日期组合都会被配对,然后计算每对日期的差值绝对值:
# 按ID合并,得到同ID的所有日期组合 merged = pd.merge(df_a, df_b, on='ID', how='left') # 计算日期差的绝对值(单位:天) merged['date_diff'] = abs(merged['Date_A'] - merged['Date_B']).dt.days
步骤3:筛选符合条件的行并选取最接近的日期
先筛选出日期差≤180天的行,然后对每个ID+Date_A组合,挑出日期差最小的那一行(也就是最接近的日期):
# 筛选日期差在180天以内的行 filtered = merged[merged['date_diff'] <= 180] # 按ID和Date_A分组,找到每组中日期差最小的行 result = filtered.loc[filtered.groupby(['ID', 'Date_A'])['date_diff'].idxmin()] # 整理结果,保留需要的列并重置索引 result = result[['ID', 'Date_A', 'Date_B']].reset_index(drop=True)
最终结果
运行完上面的代码,你会得到这样的结果:
ID Date_A Date_B 0 42 2013-04-12 2013-03-13 1 167 2010-04-19 2010-08-02 2 105 2010-12-16 2010-11-26 3 105 2012-01-05 2011-11-11
额外优化:保留所有A的行(无匹配时标记为NaN)
如果想保留DataFrame A里的所有行,哪怕没有符合条件的B行,可以调整代码,把无匹配的Date_B标记为NaT(时间类型的空值):
# 计算所有组合的日期差,不符合条件的设为无穷大 merged['date_diff'] = abs(merged['Date_A'] - merged['Date_B']).dt.days merged['date_diff'] = merged['date_diff'].where(merged['date_diff'] <= 180, float('inf')) # 找到每个A行的最小日期差对应的B行 result_with_all_a = merged.loc[merged.groupby(['ID', 'Date_A'])['date_diff'].idxmin()] # 把无匹配的Date_B设为NaN result_with_all_a['Date_B'] = result_with_all_a['Date_B'].where(result_with_all_a['date_diff'] != float('inf'), pd.NaT) # 整理结果 result_with_all_a = result_with_all_a[['ID', 'Date_A', 'Date_B']].reset_index(drop=True)
这时候结果会包含A中所有行,比如ID=42的2012-07-21对应的Date_B就会显示为NaT,代表没有符合条件的匹配项。
内容的提问来源于stack exchange,提问作者sar
相关产品推荐
相关产品推荐

