如何使用Pandas按不同列的is_start与is_end标记值对行进行分组?
高效分组解决方案(无循环)
这是一个典型的区间分组需求,完全可以用pandas的向量化操作实现,不需要低效的iterrows循环。下面是完整的解决方案,包含闭合组提取和未完成组识别:
步骤1:构造示例DataFrame
首先我们先还原你的数据:
import pandas as pd data = { 'Date': ['2021-07-16 10:40', '2021-07-16 10:41', '2021-07-16 10:42', '2021-07-16 10:43', '2021-07-16 10:44', '2021-07-16 10:45', '2021-07-16 10:46', '2021-07-16 10:47', '2021-07-16 10:48', '2021-07-16 10:49', '2021-07-16 10:50', '2021-07-16 10:51', '2021-07-16 10:52', '2021-07-16 10:53', '2021-07-16 10:54', '2021-07-16 10:55'], 'is_start': [False, False, False, True, False, False, False, True, False, False, False, False, False, False, True, False], 'is_end': [False, False, False, False, False, True, False, False, False, False, False, True, False, False, False, False] } df = pd.DataFrame(data) df['Date'] = pd.to_datetime(df['Date'])
步骤2:生成分组标识(完全向量化)
我们通过累积计数和条件筛选来标记每个行所属的组,全程无循环:
# 1. 计算起始和结束的累积次数 start_cum = df['is_start'].cumsum() end_cum = df['is_end'].cumsum() # 2. 初始标记组ID:仅保留未被结束的区间(包括结束行) df['group_id'] = start_cum.where(start_cum >= end_cum, pd.NA) # 3. 清除结束行到下一个起始行之间的无效组ID end_mask = df['is_end'].shift(fill_value=False) in_between = end_mask.cumsum() - df['is_start'].cumsum() df['group_id'] = df['group_id'].where(in_between == 0, pd.NA)
步骤3:提取闭合组和未完成组
现在我们可以轻松分离出已完成的组和未完成的组:
# 提取已闭合的组(包含is_end标记的组) closed_group_ids = df[df['is_end']]['group_id'].unique() closed_groups = df[df['group_id'].isin(closed_group_ids)].groupby('group_id') # 提取未完成的组(最后一个start之后没有对应end的区间) unfinished_group = df[~df['group_id'].isin(closed_group_ids)].dropna(subset=['group_id']) # 输出结果 print("=== 闭合分组 ===") for name, group in closed_groups: print(f"Group {int(name)}") print(group[['Date', 'is_start', 'is_end']]) print("---") if not unfinished_group.empty: print("\n=== 未完成分组 ===") print(unfinished_group[['Date', 'is_start', 'is_end']])
输出结果
运行上述代码后,你会得到:
=== 闭合分组 === Group 1 Date is_start is_end 3 2021-07-16 10:43:00 True False 4 2021-07-16 10:44:00 False False 5 2021-07-16 10:45:00 False True --- Group 2 Date is_start is_end 7 2021-07-16 10:47:00 True False 8 2021-07-16 10:48:00 False False 9 2021-07-16 10:49:00 False False 10 2021-07-16 10:50:00 False False 11 2021-07-16 10:51:00 False True --- === 未完成分组 === Date is_start is_end 14 2021-07-16 10:54:00 True False 15 2021-07-16 10:55:00 False False
原理说明
start_cum和end_cum分别记录到当前行为止的起始标记和结束标记的累积次数,通过比较两者的大小,我们可以判断当前行是否处于一个未结束的区间内。in_between标记那些位于“结束标记之后、下一个起始标记之前”的无效行,确保这些行不会被错误地归入任何组。- 最后通过筛选包含
is_end的组ID,我们精准分离出已完成的闭合组,剩下的就是未完成的组。
这种方法完全利用pandas的向量化运算,效率远高于循环,非常适合处理大规模数据集。
内容的提问来源于stack exchange,提问作者HapiDaze
相关产品推荐
相关产品推荐

