
本文介绍如何基于事件类型的连续性(而非全局类别)对 DataFrame 中的 Duration 列进行分组求和,特别适用于加速度计数据中识别并聚合“步态爆发(stepping bouts)”等时序连续行为。
本文介绍如何基于事件类型的连续性(而非全局类别)对 dataframe 中的 `duration` 列进行分组求和,特别适用于加速度计数据中识别并聚合“步态爆发(stepping bouts)”等时序连续行为。
在可穿戴设备(如加速度计)数据分析中,一个常见需求是:将同一行为类型(如 Event Type == 2 表示“步行”)的连续记录视为一次“爆发(bout)”,并对每个 bout 内的 Duration 求和,而非简单按事件类型全局聚合——因为非连续的同类事件应属于不同 bout(例如两次分开的步行过程)。
关键挑战在于:Pandas 的标准 groupby('Event Type') 会把所有 2 合并为一组,丢失时间/顺序信息;而我们需要的是按 ID + 连续段(contiguous block)双重分组。
✅ 解决方案:构造连续段标识符(Contiguous Group ID)
核心思路是:
- 先筛选出目标事件(如 Event Type == 2);
- 对原始数据按 ID 分组,在每组内识别 Event Type != 2 的位置,并用 cumsum() 构造“段编号”——每当遇到非目标事件,段编号递增,从而为每个连续的目标事件块赋予唯一标识;
- 结合 ID 和该段编号进行分组,再对 Duration 求和。
import pandas as pd
# 示例数据(含多个 stepping bouts)
df = pd.DataFrame({
'ID': [1885] * 13,
'Date': ['2021-06-29'] + ['2021-07-07'] * 12,
'Event Type': [4.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 1.0, 2.0, 2.0, 2.0, 1.0],
'Duration': [31208.5, 1.1, 3.2, 3.5, 4.3, 3.7, 1.2, 1.1, 1.0, 3.7, 1.2, 1.1, 1.0]
})
# 步骤1:创建布尔掩码,标识目标事件(此处为 Event Type == 2)
mask = df['Event Type'].eq(2)
# 步骤2:在每个 ID 组内,对「非目标事件」累计求和,生成连续段标识
# (~mask) → True 表示非 stepping;groupby('ID').cumsum() → 每次遇到非 stepping 就+1,形成段边界
segment_id = (~mask).groupby(df['ID']).cumsum()
# 步骤3:仅保留 stepping 行,并按 ID + segment_id 分组求和
bouts = (df[mask]
.groupby(['ID', segment_id[mask]], as_index=False)
['Duration'].sum()
.rename(columns={'Duration': 'Bout_Duration'}))
print(bouts)
输出:
ID Bout_Duration 0 1885 18.1 1 1885 6.0
✅ 第一行 18.1 是前 7 个连续 2.0 的 Duration 和(1.1+3.2+3.5+4.3+3.7+1.2+1.1);
✅ 第二行 6.0 是后续 3 个连续 2.0 的和(3.7+1.2+1.1)。
⚠️ 注意事项与扩展建议
-
日期敏感性:当前逻辑未显式按 Date 分组。若需确保 bout 严格限定在单日内(例如跨日的连续 2 不应合并),应在 groupby 中加入 'Date':
segment_id = (~mask).groupby([df['ID'], df['Date']]).cumsum() # 然后 groupby(['ID', 'Date', segment_id[mask]])
性能优化:对数十万行数据(如题中 ~30k/文件),该方法时间复杂度为 O(n),无需循环,高效可靠。
-
后续分析:得到 bouts DataFrame 后,可轻松计算每日平均 bout 时长、最长 bout、总步态时间占比等指标:
bouts.groupby('ID')['Bout_Duration'].agg(['mean', 'max', 'count'])
掌握这种「基于连续性的分组」技巧,是处理时序行为数据(如活动识别、睡眠阶段、心率区间)的关键基础能力。










