
本文介绍如何高效计算 DataFrame 每一列中各唯一值的总连续存在时长,即对所有相同值的连续段分别求时间跨度(末次出现时间 − 首次出现时间),再累加各段时长,最终输出结构化报告。
本文介绍如何高效计算 dataframe 每一列中各唯一值的**总连续存在时长**,即对所有相同值的连续段分别求时间跨度(末次出现时间 − 首次出现时间),再累加各段时长,最终输出结构化报告。
在时序型数据处理中,常需分析某状态(如传感器读数、设备模式、日志标签)在每一列中连续维持的时间总和。例如:colA=2 在时间点 1.1–2.2 出现一次,在 8.5–11.2 再次出现,则其总持续时长为 (2.2−1.1) + (11.2−8.5) = 3.8s。关键在于识别“连续块”(consecutive runs),而非全局去重统计。
Pandas 提供了无需显式循环的向量化方案,核心思路是:
✅ 熔解(melt):将多列状态统一转为长格式,便于统一处理;
✅ 标记连续块:利用 Series.ne(Series.shift()).cumsum() 为每一段相同值分配唯一组 ID;
✅ 分组聚合:先按列名、值、块 ID 三重分组,提取每块的起始/结束时间;
✅ 汇总求和:再按列名和值二次分组,对各块时长求和。
以下为完整可运行代码:
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]
})
# 核心计算逻辑
df_long = df.melt(id_vars='Time', var_name='Col_name', value_name='unique_value')
group_id = df_long['unique_value'].ne(df_long['unique_value'].shift()).cumsum().rename('run_id')
result = (
df_long
.groupby(['Col_name', 'unique_value', group_id])['Time']
.agg(['first', 'last'])
.assign(Duration=lambda x: x['last'] - x['first'])
.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
? 注意事项与技巧:
- Time 列必须为数值型(如 float64 或 datetime64),若为字符串时间需先转换(如 pd.to_datetime());
- 若需保留单位(如 "3.8s"),可在最后添加 .astype(str) + 's',但建议保持数值类型便于后续计算;
- 此方法天然支持任意数量的状态列(只需确保 id_vars=['Time'] 正确指定时间列);
- 对于超大数据集,该方案仍保持高效,因全程使用 Pandas 原生向量化操作,避免 Python 层循环。
最终结果可直接导出为 CSV:
result.to_csv('duration_report.csv', index=False)
该方案简洁、健壮、可扩展,是处理“状态持续时长”类问题的标准范式。











