
本文介绍如何高效计算dataframe中每列各唯一值的总持续时间,即对每个连续出现的值段,用该段末尾与起始时间之差求和,最终生成包含列名、唯一值及总时长的汇总报表。
本文介绍如何高效计算dataframe中每列各唯一值的总持续时间,即对每个连续出现的值段,用该段末尾与起始时间之差求和,最终生成包含列名、唯一值及总时长的汇总报表。
在时序型或状态记录类数据中(如传感器读数、设备状态日志),常需统计某状态“持续了多久”——但注意:不是该值在整个数据集中出现的总时间跨度,而是所有连续出现片段的持续时间之和。例如,colA中值2在开头连续出现3行(时间1.1→3.4),之后又在末尾连续出现4行(8.5→11.2),其总持续时间为 (3.4−1.1) + (11.2−8.5) = 2.3 + 2.7 = 5.0 秒(注:示例原始计算有误,正确应为 2.3 + 2.7 = 5.0;但答案代码逻辑无误,实际输出 3.8 对应原题数据中 2 的两段为 [1.1,2.2] 和 [8.5,11.2],即 1.1 + 2.7 = 3.8)。
核心思路是:将状态变化视为“分组边界”,对每个连续段独立计算时长,再按值聚合求和。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:长格式转换(melt),统一处理所有列
df_long = df.melt(id_vars='Time', var_name='Col_name', value_name='unique_value')
# 步骤 2:为每个连续相同值生成唯一组ID(关键!)
# .ne() 检查是否不等于前一行,cumsum() 累计生成新组号
group_id = df_long['unique_value'].ne(df_long['unique_value'].shift()).cumsum()
df_long['group'] = group_id
# 步骤 3:按列名、值、连续组三重分组,提取每段首尾时间
segment_durations = (
df_long.groupby(['Col_name', 'unique_value', 'group'])['Time']
.agg(first='first', last='last')
.assign(Duration=lambda x: x['last'] - x['first'])
[['Duration']]
)
# 步骤 4:按列名+唯一值聚合,求总持续时间
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
? 关键步骤解析
- df.melt('Time'):将宽表转为长表,使 colA/colB 成为变量名(variable),值统一为 value 列,便于跨列统一处理。
- df_long['value'].ne(df_long['value'].shift()).cumsum():
- .shift() 下移一行 → 与上一行比较;
- .ne() 即 !=,返回 True 表示值发生变化;
- .cumsum() 将每次变化标记为新组起点,生成连续段标识 group(如 [1,1,2,2,3,3,3,4,4,4,4])。
- groupby(['Col_name','unique_value','group']).agg(...):确保同一列、同一值、同一连续段被归为一组,精准提取该段起止时间。
- 最后一层 groupby(['Col_name','unique_value']).sum():合并同一值的所有连续段时长。
⚠️ 注意事项
- 时间列必须有序:Time 列需按升序排列,否则 first/last 无物理意义。若未排序,先执行 df = df.sort_values('Time').reset_index(drop=True)。
- 缺失值处理:若 Time 或目标列含 NaN,ne().cumsum() 可能异常。建议提前清洗:df = df.dropna(subset=['Time'] + target_cols)。
-
输出单位:结果为数值型(秒),如需带单位字符串(如 "3.8s"),可追加:
result['Duration'] = result['Duration'].round(1).astype(str) + 's' - 性能优势:相比循环或 apply,本方法全程向量化,处理百万级行数据仍高效。
此方案兼具可读性、健壮性与扩展性,是分析状态持续时间的标准实践。

















