如何按id合并连续日期且属性相同的Pandas重复记录?
问题描述
我有如下数据:
import pandas as pd have = {'id': [1,1,1], 'start_date': ['2014-12-01 00:00:00', '2015-03-01 00:00:00', '2015-06-01 00:00:00'], 'end_date': ['2015-02-28 23:59:59', '2015-05-31 23:59:59', '2015-08-02 23:59:59'], 'attr_1': ['Z', 'Z', 'Z'], 'attr_2': ['A', 'A', ''], 'attr_3': ['B', 'B', '']} have = pd.DataFrame(data=have)
初始数据输出:
id start_date end_date attr_1 attr_2 attr_3 0 1 2014-12-01 00:00:00 2015-02-28 23:59:59 Z A B 1 1 2015-03-01 00:00:00 2015-05-31 23:59:59 Z A B 2 1 2015-06-01 00:00:00 2015-08-02 23:59:59 Z
需要按每个id合并记录,满足两个条件:
- 下一条记录的
start_date等于上一条记录的end_date + 1秒(时间区间连续) - 所有
attr_列的取值完全相同
期望得到的结果:
want = {'id': [1,1], 'start_date': ['2014-12-01 00:00:00', '2015-06-01 00:00:00'], 'end_date': ['2015-05-31 23:59:59', '2015-08-02 23:59:59'], 'attr_1': ['Z', 'Z'], 'attr_2': ['A', ''], 'attr_3': ['B', '']} want = pd.DataFrame(data=want)
结果输出:
id start_date end_date attr_1 attr_2 attr_3 0 1 2014-12-01 00:00:00 2015-05-31 23:59:59 Z A B 1 1 2015-06-01 00:00:00 2015-08-02 23:59:59 Z
实际场景包含百万级记录、数千个id以及60+个需要校验的属性列,需保证处理效率。
解决方案
步骤说明
- 转换日期类型:将
start_date和end_date转为datetime类型,支持时间运算。 - 自动识别属性列:提取所有以
attr_开头的列,无需手动指定60+列。 - 计算连续条件:按
id分组,判断相邻记录是否同时满足「时间连续」和「属性完全一致」。 - 生成聚合分组标签:将连续满足条件的记录归为同一组,用于后续合并。
- 聚合得到最终结果:按
id和分组标签聚合,取每组最早的start_date、最晚的end_date,属性列取组内任意值(同组属性一致)。
高效实现代码
import pandas as pd # 1. 转换日期列为datetime类型 have['start_date'] = pd.to_datetime(have['start_date']) have['end_date'] = pd.to_datetime(have['end_date']) # 2. 自动提取所有属性列 attr_cols = [col for col in have.columns if col.startswith('attr_')] # 3. 计算时间连续条件:当前行start_date等于上一行end_date+1秒 have['prev_end'] = have.groupby('id')['end_date'].shift(1) time_continuous = have['start_date'] == have['prev_end'] + pd.Timedelta(seconds=1) # 4. 计算属性相同条件:当前行与上一行所有属性列值完全一致 attr_same = have.groupby('id')[attr_cols].apply(lambda x: x.eq(x.shift(1)).all(axis=1)).reset_index(drop=True) # 5. 生成分组标签:不满足连续条件时,分组键递增 have['group_key'] = (~(time_continuous & attr_same)).cumsum() # 6. 聚合合并记录 result = have.groupby(['id', 'group_key']).agg( start_date=('start_date', 'min'), end_date=('end_date', 'max'), **{col: (col, 'first') for col in attr_cols} ).reset_index(drop=True) print(result)
关键优化点
- 全程采用Pandas向量化操作,避免循环,适配百万级数据处理需求。
- 自动识别属性列,减少代码维护成本。
- 分组标签生成采用累计求和逻辑,计算高效且逻辑清晰。
内容的提问来源于stack exchange,提问作者legends1337
相关产品推荐
相关产品推荐

