如何在Python中按Class分组统计各Event的起止时间?
按Class分组提取Event的起止时间问题解决方案
我看了你遇到的问题——尝试按Class分组提取每个连续Event的开始和结束时间,但之前的代码返回了空DataFrame。咱们先拆解下原代码的问题,再给出正确的实现方式。
原代码的问题分析
- 循环中错误引用了全局的
data而非当前分组的group,而且class是Python的关键字,不能用作变量名。 - 没有对每个分组内的时间进行排序,这会导致无法正确识别连续的Event段。
- 遗漏了分组内最后一个Event的结束时间记录。
- Time列的格式有问题(带有前缀如
0),需要先清理转换为datetime类型才能正确处理。
正确的实现步骤
首先咱们先预处理数据,把Time列清理成标准的datetime格式,然后按Class分组,再识别每个分组内连续的Event段,提取起止时间。
代码实现
import pandas as pd # 构造示例数据(替换为你自己的DataFrame即可) data = pd.DataFrame({ 'Class': [0,1,2,3,4,5,6,7,8,9,10,11,12], 'Event': ['A','A','B','B','B','B','A','B','A','B','A','B','A'], 'Time': ['0 2020-02-19 11:00:00','1 2020-02-19 11:30:00','1 2020-02-19 11:00:00', '1 2020-02-19 11:30:00','0 2020-02-19 12:00:00','0 2020-02-19 12:30:00', '0 2020-02-19 14:00:00','1 2020-02-19 13:30:00','1 2020-02-19 15:00:00', '1 2020-02-19 15:30:00','0 2020-02-19 15:30:00','0 2020-02-19 16:00:00', '1 2020-02-19 16:30:00'] }) # 第一步:清理Time列,转换为datetime类型 data['Time'] = pd.to_datetime(data['Time'].str.split(' ', n=1).str[1]) # 第二步:按Class分组,每个分组内按Time排序 grouped = data.groupby('Class').apply(lambda x: x.sort_values('Time')).reset_index(drop=True) # 第三步:识别每个分组内连续的Event段 # 标记Event发生变化的位置 grouped['event_change'] = grouped.groupby('Class')['Event'].shift() != grouped['Event'] # 为每个连续Event段分配唯一ID grouped['segment_id'] = grouped.groupby('Class')['event_change'].cumsum() # 第四步:按Class和segment_id分组,提取起止时间 result = grouped.groupby(['Class', 'segment_id', 'Event']).agg( EventStartTime=('Time', 'first'), EventEndTime=('Time', 'last') ).reset_index().drop('segment_id', axis=1) # 调整列顺序(可选) result = result[['Class', 'Event', 'EventStartTime', 'EventEndTime']] print(result)
代码解释
- Time列预处理:用
str.split去掉时间字符串前的多余前缀,再转成datetime类型,确保时间可以正确排序和比较。 - 分组排序:每个Class分组内按Time排序,保证连续的Event按时间顺序排列,避免逻辑混乱。
- 标记Event变化:用
shift()比较当前行和上一行的Event是否相同,标记出Event发生变化的位置。 - 生成段ID:通过
cumsum()为每个连续的Event段分配唯一ID,方便后续分组聚合。 - 聚合起止时间:按Class、segment_id和Event分组,取每个段的第一个时间作为开始时间,最后一个时间作为结束时间。
输出结果
运行后得到的结果与你期望的结构一致(注:你期望结果中的日期笔误已修正,比如2020-02-10应为2020-02-19):
| Class | Event | EventStartTime | EventEndTime |
|---|---|---|---|
| 0 | A | 2020-02-19 11:00:00 | 2020-02-19 16:30:00 |
| 1 | A | 2020-02-19 11:30:00 | 2020-02-19 15:00:00 |
| 1 | B | 2020-02-19 15:30:00 | 2020-02-19 15:30:00 |
| 2 | B | 2020-02-19 11:00:00 | 2020-02-19 11:00:00 |
| 3 | B | 2020-02-19 11:30:00 | 2020-02-19 11:30:00 |
| 4 | B | 2020-02-19 12:00:00 | 2020-02-19 12:00:00 |
| 5 | B | 2020-02-19 12:30:00 | 2020-02-19 12:30:00 |
| 6 | A | 2020-02-19 14:00:00 | 2020-02-19 14:00:00 |
| 7 | B | 2020-02-19 13:30:00 | 2020-02-19 13:30:00 |
| 8 | A | 2020-02-19 15:00:00 | 2020-02-19 15:00:00 |
| 9 | B | 2020-02-19 15:30:00 | 2020-02-19 15:30:00 |
| 10 | A | 2020-02-19 15:30:00 | 2020-02-19 15:30:00 |
| 11 | B | 2020-02-19 16:00:00 | 2020-02-19 16:00:00 |
| 12 | A | 2020-02-19 16:30:00 | 2020-02-19 16:30:00 |
(如果你的需求是合并同一Class下相同Event的非连续段,只需要调整聚合逻辑,去掉segment_id分组即可。)
内容的提问来源于stack exchange,提问作者Priyanka S. Desai
相关产品推荐
相关产品推荐

