
本文介绍如何高效计算 DataFrame 中每一列各唯一值的总持续时间——即该值在连续出现时段内的时间跨度之和,使用 pandas 向量化操作避免显式循环,适用于时序数据的停留时长分析。
本文介绍如何高效计算 dataframe 中每一列各唯一值的总持续时间——即该值在**连续出现时段内**的时间跨度之和,使用 pandas 向量化操作避免显式循环,适用于时序数据的停留时长分析。
在时序数据分析中,常需统计某状态(如传感器读数、设备运行模式、用户行为标签)的“实际驻留时长”。关键在于:同一值若非连续出现,应视为多个独立时间段,各自计算持续时间后累加。例如 colA 中值 2 在索引 0–1 和 7–10 两次出现,需分别计算 (2.2 − 1.1) 和 (11.2 − 8.5),再求和得 3.8s。
以下为完整、可复用的解决方案,基于 pandas 链式操作实现:
✅ 核心思路:熔解 → 标记连续段 → 分组聚合 → 求和
import pandas as pd
# 示例数据
df = pd.DataFrame({
'Time': [1.1, 2.2, 3.4, 4.5, 5.6, 6.2, 7.4, 8.5, 9.8, 10.1, 11.2],
'colA': [2, 2, 3, 3, 4, 4, 4, 2, 2, 2, 2],
'colB': [2, 2, 5, 5, 5, 6, 6, 6, 5, 5, 5]
})
# 步骤 1:将除 Time 外的所有列熔解为长格式
df_long = df.melt(id_vars='Time', var_name='Col_name', value_name='unique_value')
# 步骤 2:为每个连续相同值序列生成唯一组标识符
df_long['group_id'] = df_long['unique_value'].ne(df_long['unique_value'].shift()).cumsum()
# 步骤 3:按列名、值、连续组三重分组,提取每段起止时间
segment_durations = (
df_long.groupby(['Col_name', 'unique_value', 'group_id'])['Time']
.agg(first='min', last='max') # 确保按 Time 排序后 first/last 准确(实际已有序)
.assign(Duration=lambda x: x['last'] - x['first'])
)
# 步骤 4:去除 group_id 维度,按列名与值汇总总持续时间
result = (
segment_durations.groupby(['Col_name', 'unique_value'])['Duration']
.sum()
.reset_index()
)
print(result)
输出结果:
Col_name unique_value Duration 0 colA 2 3.8 1 colA 3 1.1 2 colA 4 1.8 3 colB 2 1.1 4 colB 5 3.6 5 colB 6 2.3
? 关键技术点说明
- melt():将宽表转为长表,统一处理所有目标列,避免逐列循环;
- ne().shift().cumsum():检测值变化并生成连续段 ID(True 表示新段开始),是识别“连续块”的核心技巧;
- 双重 groupby:先按 (列名, 值, 连续段) 计算单段时长;再按 (列名, 值) 汇总,确保跨断点的同值时长被正确累加;
- agg(['min','max']) 替代 first/last 更鲁棒:即使原始数据未严格按 Time 排序,也能保证时段边界准确(推荐用于生产环境)。
⚠️ 注意事项
- 时间列必须单调递增:本方法假设 Time 列已升序排列;若无序,请先执行 df = df.sort_values('Time').reset_index(drop=True);
- 浮点精度问题:Duration 为 float64,如需保留小数位(如 .2f),可用 result['Duration'] = result['Duration'].round(2);
- 导出 CSV:直接调用 result.to_csv('duration_report.csv', index=False) 即可生成要求格式的报告文件。
该方案完全向量化、无 Python 循环,兼具可读性与高性能,适用于万行级时序数据的批量分析。











