如何在Pandas中按30天时间间隔分组DataFrame行并生成事件起止日期?
问题:按时间跨度对DataFrame行分组
需要对DataFrame中的行按时间跨度分类:时间差小于30天的行归为同一事件组,大于30天的视为独立事件。每组起始日期为组内第一条记录的start日期,结束日期为组内最后一条记录的start日期,未合并的事件起止日期相同。
示例DataFrame
| id | start |
|---|---|
| 1 | 2023-08-10 |
| 1 | 2023-09-20 |
| 1 | 2023-12-20 |
| 2 | 2023-08-15 |
| 2 | 2023-09-20 |
| 2 | 2023-11-25 |
期望转换结果
| id | start | end |
|---|---|---|
| 1 | 2023-08-10 | 2023-09-20 |
| 1 | 2023-12-20 | 2023-12-20 |
| 2 | 2023-08-15 | 2023-09-20 |
| 2 | 2023-11-25 | 2023-11-25 |
测试数据代码
df = pd.DataFrame({ 'id': [1, 2, 1, 1, 2, 2], 'start': ['2023-08-10', '2023-08-15', '2023-09-20', '2023-11-20', '2023-09-20', '2023-11-01'], })
尝试的代码(未达到预期)
delta = '30D' # ensure datetime df['start'] = df['start'].apply(pd.to_datetime) # create new column df['end'] = df['start'] # group values that are within 30D in a group group = (df['start'].sub(df.groupby('id')['end'].shift()).gt(delta).groupby(df['id']).cumsum()) # aggregate out = (df.groupby(['id', group], as_index=False).agg({'start': 'min', 'end': 'max'}))
解决方案
原代码的核心问题是分组逻辑错误:误用初始等于start的end列做偏移计算,且未正确按id分组后计算连续行的时间差。正确实现步骤如下:
- 将
start列转为datetime类型,并按id和start排序(保证时间顺序正确) - 按
id分组,计算当前行start与上一行start的时间差 - 当时间差大于30天或为组内第一行时,标记为新组
- 按
id累加新组标记生成组编号,最后分组聚合得到每组的起止日期
完整代码:
import pandas as pd df = pd.DataFrame({ 'id': [1, 2, 1, 1, 2, 2], 'start': ['2023-08-10', '2023-08-15', '2023-09-20', '2023-11-20', '2023-09-20', '2023-11-01'], }) # 转换时间格式并按id、时间排序 df['start'] = pd.to_datetime(df['start']) df = df.sort_values(['id', 'start']).reset_index(drop=True) # 计算同id下当前行与上一行的时间差 df['time_diff'] = df.groupby('id')['start'].diff() # 标记新组:时间差超30天或为组内首行 df['new_group'] = (df['time_diff'] > pd.Timedelta(days=30)) | df['time_diff'].isna() # 生成组编号 df['group_id'] = df.groupby('id')['new_group'].cumsum() # 分组聚合得到最终结果 result = df.groupby(['id', 'group_id'], as_index=False).agg( start=('start', 'min'), end=('start', 'max') ).drop(columns='group_id') print(result)
运行输出:
id start end 0 1 2023-08-10 2023-09-20 1 1 2023-11-20 2023-11-20 2 2 2023-08-15 2023-09-20 3 2 2023-11-01 2023-11-01
内容的提问来源于stack exchange,提问作者Grigoris Papapostolou
相关产品推荐
相关产品推荐

