如何合并Pandas DataFrame产品记录:按状态及日期间隔聚合
需求与实现方案
原始数据
| Product | Status | Start_Date | End_Date |
|---|---|---|---|
| Banana | Good | 2000-01-01 | 2000-12-31 |
| Apple | Good | 2000-01-01 | 2000-12-31 |
| Apple | Good | 2001-01-01 | 2001-12-31 |
| Apple | Good | 2002-01-01 | 2023-09-28 |
| Banana | Bad | 2001-01-01 | 2001-12-31 |
| Banana | Good | 2002-01-01 | NaT |
| Orange | Good | 2001-01-01 | 2001-12-31 |
| Orange | Good | 2002-02-01 | NaT |
需求
- 合并无状态变化且时间连续的产品记录,保留最小
Start_Date和最大End_Date - 状态切换时,单独保留该状态段的聚合记录
- 相邻记录存在日期间隔时,保留各段独立记录
期望输出
| Product | Status | Start_Date | End_Date |
|---|---|---|---|
| Apple | Good | 2000-01-01 | 2023-09-28 |
| Banana | Good | 2000-01-01 | 2000-12-31 |
| Banana | Bad | 2001-01-01 | 2001-12-31 |
| Banana | Good | 2002-01-01 | NaT |
| Orange | Good | 2001-01-01 | 2001-12-31 |
| Orange | Good | 2002-02-01 | NaT |
实现代码
import pandas as pd # 1. 构建原始DataFrame data = { 'Product': ['Banana', 'Apple', 'Apple', 'Apple', 'Banana', 'Banana', 'Orange', 'Orange'], 'Status': ['Good', 'Good', 'Good', 'Good', 'Bad', 'Good', 'Good', 'Good'], 'Start_Date': ['2000-01-01', '2000-01-01', '2001-01-01', '2002-01-01', '2001-01-01', '2002-01-01', '2001-01-01', '2002-02-01'], 'End_Date': ['2000-12-31', '2000-12-31', '2001-12-31', '2023-09-28', '2001-12-31', pd.NaT, '2001-12-31', pd.NaT] } df = pd.DataFrame(data) # 2. 转换日期列为datetime类型,便于时间运算 df['Start_Date'] = pd.to_datetime(df['Start_Date']) df['End_Date'] = pd.to_datetime(df['End_Date']) # 3. 按产品和开始日期排序,确保时间顺序正确 df_sorted = df.sort_values(['Product', 'Start_Date']).reset_index(drop=True) # 4. 生成分组标识:满足任一条件则开启新分组 # - 产品切换(冗余但保证鲁棒性) # - 状态切换 # - 当前记录的开始日期不是上一条记录结束日期的次日(存在间隔) df_sorted['group_key'] = ( (df_sorted['Product'] != df_sorted['Product'].shift()) | (df_sorted['Status'] != df_sorted['Status'].shift()) | (df_sorted['Start_Date'] != df_sorted['End_Date'].shift() + pd.Timedelta(days=1)) ).cumsum() # 5. 聚合分组,取每组的最小开始日期和最大结束日期 result = df_sorted.groupby(['Product', 'Status', 'group_key'], as_index=False).agg( Start_Date=('Start_Date', 'min'), End_Date=('End_Date', 'max') ).drop(columns='group_key') # 6. 按产品和开始日期排序,匹配期望输出顺序 result = result.sort_values(['Product', 'Start_Date']).reset_index(drop=True) print(result)
代码说明
- 日期转换:将字符串日期转为datetime类型,支持后续的时间差计算;
- 排序:确保每个产品的记录按时间顺序排列,保证分组逻辑的正确性;
- 分组标识:通过
cumsum()给满足拆分条件的记录分配不同组ID,实现"连续同状态且无间隔则合并"的逻辑; - 聚合:对每个分组取最小开始日期和最大结束日期,完成合并;
- 最终排序:调整输出顺序,与期望结果一致。
内容的提问来源于stack exchange,提问作者PRuss
相关产品推荐
相关产品推荐

