如何在Pandas中合并存在日期重叠的两个DataFrame?
处理Pandas中带重叠日期的DataFrame合并问题
要实现两个带重叠日期区间的DataFrame合并,核心思路是先拆分出所有不重叠的连续日期区间,再为每个区间匹配对应的数据值。以下是具体步骤:
步骤1:预处理日期列
首先将原始数据中的日期字符串转为Pandas的datetime类型,方便后续时间运算:
import pandas as pd # 构造示例数据 dds = pd.DataFrame({ 'STATE': ['Alabama', 'Alabama', 'Alabama'], 'START_DATE': ['04/01/2021', '06/16/2021', '08/13/2021'], 'END_DATE': ['06/15/2021', '08/12/2021', '09/30/2021'], 'data_val': ['x', 'y', 'z'] }) ops = pd.DataFrame({ 'STATE': ['Alabama', 'Alabama', 'Alaska'], 'START_DATE': ['05/01/2021', '06/01/2021', '04/01/2021'], 'END_DATE': ['05/31/2021', '12/01/2021', '08/01/2021'], 'data_val2': ['ab', 'cd', 'ez'] }) # 转换日期列为datetime类型,自动推断格式 for df in [dds, ops]: df['START_DATE'] = pd.to_datetime(df['START_DATE'], infer_datetime_format=True) df['END_DATE'] = pd.to_datetime(df['END_DATE'], infer_datetime_format=True)
注:原始ops数据中的31/05/2021应为输入格式错误,调整为05/31/2021以匹配月/日/年的统一格式。
步骤2:拆分并匹配日期区间
按STATE分组,收集所有原始区间的起止点(包括区间结束日+1天),拆分出所有不重叠的连续小区间,再为每个小区间匹配对应的data_val和data_val2:
result_list = [] # 按STATE遍历处理每个州的数据 for state in pd.concat([dds['STATE'], ops['STATE']]).unique(): # 获取当前州的所有日期分割点:包含原始区间的开始日、结束日+1天 dds_dates = dds[dds['STATE'] == state][['START_DATE', 'END_DATE']] ops_dates = ops[ops['STATE'] == state][['START_DATE', 'END_DATE']] split_dates = sorted(set( dds_dates['START_DATE'].tolist() + (dds_dates['END_DATE'] + pd.Timedelta(days=1)).tolist() + ops_dates['START_DATE'].tolist() + (ops_dates['END_DATE'] + pd.Timedelta(days=1)).tolist() )) # 生成每个连续的小区间 for start, next_start in zip(split_dates[:-1], split_dates[1:]): end = next_start - pd.Timedelta(days=1) if start > end: continue # 匹配当前区间对应的data_val dds_match = dds[(dds['STATE'] == state) & (dds['START_DATE'] <= start) & (dds['END_DATE'] >= end)] data_val = dds_match['data_val'].iloc[0] if not dds_match.empty else None # 匹配当前区间对应的data_val2 ops_match = ops[(ops['STATE'] == state) & (ops['START_DATE'] <= start) & (ops['END_DATE'] >= end)] data_val2 = ops_match['data_val2'].iloc[0] if not ops_match.empty else None result_list.append({ 'STATE': state, 'START_DATE': start, 'END_DATE': end, 'data_val': data_val, 'data_val2': data_val2 }) # 转换为结果DataFrame result = pd.DataFrame(result_list)
步骤3:格式化输出
将日期转为指定格式,空值替换为NULL,匹配期望输出:
# 格式化日期为MM/DD/YYYY result['START_DATE'] = result['START_DATE'].dt.strftime('%m/%d/%Y') result['END_DATE'] = result['END_DATE'].dt.strftime('%m/%d/%Y') # 替换空值为NULL result = result.fillna('NULL') print(result)
运行后输出结果与期望一致:
STATE START_DATE END_DATE data_val data_val2 0 Alabama 04/01/2021 04/30/2021 x NULL 1 Alabama 05/01/2021 05/31/2021 x ab 2 Alabama 06/01/2021 06/15/2021 x cd 3 Alabama 06/16/2021 08/12/2021 y cd 4 Alabama 08/13/2021 09/30/2021 z cd 5 Alabama 10/01/2021 12/01/2021 NULL cd 6 Alaska 04/01/2021 08/01/2021 NULL ez
内容的提问来源于stack exchange,提问作者NEWBIE
相关产品推荐
相关产品推荐

