时间序列分组需求:按连续阶段统计项目阶段起止日期
按连续阶段统计项目起止日期的解决方案
问题背景
现有项目阶段时间数据如下:
| project | stage | date |
|---|---|---|
| 33 | New | 3-sep-2022 |
| 33 | New | 10-sep-2022 |
| 33 | Preparation | 11-sep-2022 |
| 33 | Preparation | 21-sep-2022 |
| 33 | Preparation | 23-sep-2022 |
| 33 | New | 24-sep-2022 |
| 33 | New | 28-sep-2022 |
需要统计每个连续阶段的起止日期,期望输出:
| project | stage | begin_stage | end_stage |
|---|---|---|---|
| 33 | New | 3-sep-2022 | 10-sep-2022 |
| 33 | Preparation | 11-sep-2022 | 23-sep-2022 |
| 33 | New | 24-sep-2022 | 28-sep-2022 |
注意:项目在9月24日回到New阶段,需将该阶段拆分为两次统计。
尝试了以下代码,但会把所有New阶段合并,不符合需求:
min_df = df.groupby(['project', 'stage'], as_index=False)['date'].agg('min') max_df = df.groupby(['project', 'stage'], as_index=False)['date'].agg('max') df = pd.merge(min_df, max_df, how='left', on=['project', 'stage']) df.rename(columns={'date_x': 'begin_state', 'date_y': 'end_state'}, inplace=True)
错误输出:
| project | stage | begin_stage | end_stage |
|---|---|---|---|
| 33 | New | 3-sep-2022 | 28-sep-2022 |
| 33 | Preparation | 11-sep-2022 | 23-sep-2022 |
解决方案
核心思路是识别连续的阶段分组,通过标记阶段变化的位置生成分组ID,再按分组ID聚合统计起止日期。
步骤1:转换日期格式并排序
先将date列转为datetime类型,确保时间顺序正确:
import pandas as pd # 构造示例数据 data = [ [33, 'New', '3-sep-2022'], [33, 'New', '10-sep-2022'], [33, 'Preparation', '11-sep-2022'], [33, 'Preparation', '21-sep-2022'], [33, 'Preparation', '23-sep-2022'], [33, 'New', '24-sep-2022'], [33, 'New', '28-sep-2022'] ] df = pd.DataFrame(data, columns=['project', 'stage', 'date']) # 转换日期格式 df['date'] = pd.to_datetime(df['date'], format='%d-%b-%Y') # 按项目和日期排序 df = df.sort_values(['project', 'date'])
步骤2:生成连续阶段的分组ID
通过对比当前行与上一行的stage是否一致,标记阶段变化点,再按项目累加生成唯一分组ID:
# 标记阶段变化的行 df['stage_change'] = df['stage'] != df['stage'].shift(1) # 生成连续阶段的分组ID df['group_id'] = df.groupby('project')['stage_change'].cumsum()
步骤3:聚合统计起止日期
按project、stage和group_id分组,提取每个组的最小(开始日期)和最大(结束日期)值,最后格式化日期并清理无关列:
# 聚合统计 result = df.groupby(['project', 'stage', 'group_id'], as_index=False).agg( begin_stage=('date', 'min'), end_stage=('date', 'max') ) # 格式化日期为原始格式 result['begin_stage'] = result['begin_stage'].dt.strftime('%d-%b-%Y').str.lower() result['end_stage'] = result['end_stage'].dt.strftime('%d-%b-%Y').str.lower() # 移除分组ID列 result = result.drop('group_id', axis=1) print(result)
最终输出
project stage begin_stage end_stage 0 33 New 03-sep-2022 10-sep-2022 1 33 Preparation 11-sep-2022 23-sep-2022 2 33 New 24-sep-2022 28-sep-2022
内容的提问来源于stack exchange,提问作者Stephany
相关产品推荐
相关产品推荐

