You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pandas:计算同一列中各时间段块的时间差总和

计算按连续时间段分块的ID总时长

我有如下DataFrame,想要计算每个ID对应的总时长。不能直接使用.max() - .min(),需要将连续的时间段分块后分别计算再求和,例如:06:00:01至06:00:32为31秒,19:53:29至19:54:52为1分23秒,01:58:00至1:58:50为50秒,总计164秒。

ID No.  TimeStamp
18507   19/04/2023 6:00:01
18507   19/04/2023 6:00:02
18507   19/04/2023 6:00:04
18507   19/04/2023 6:00:06
18507   19/04/2023 6:00:09
18507   19/04/2023 6:00:12
18507   19/04/2023 6:00:14
18507   19/04/2023 6:00:17
18507   19/04/2023 6:00:26
18507   19/04/2023 6:00:28
18507   19/04/2023 6:00:30
18507   19/04/2023 6:00:32
18507   19/04/2023 19:53:29
18507   19/04/2023 19:53:39
18507   19/04/2023 19:53:41
18507   19/04/2023 19:53:48
18507   19/04/2023 19:53:55
18507   19/04/2023 19:53:56
18507   19/04/2023 19:54:04
18507   19/04/2023 19:54:06
18507   19/04/2023 19:54:52
18507   20/04/2023 1:58:00
18507   20/04/2023 1:58:05
18507   20/04/2023 1:58:05
18507   20/04/2023 1:58:05
18507   20/04/2023 1:58:09
18507   20/04/2023 1:58:36
18507   20/04/2023 1:58:38
18507   20/04/2023 1:58:46
18507   20/04/2023 1:58:50

我的思路是通过df['diff'] = (df['TimeStamp'] - df['TimeStamp'].shift(1))创建判断边界,再用df['diff_truth'] = df['diff'] < '01:00:00'标记连续时间段。

标记后的部分结果:

............
18507   19/04/2023 6:00:28  0 days 00:00:06  True
18507   19/04/2023 6:00:30  0 days 00:00:06  True
18507   19/04/2023 6:00:32  0 days 00:00:08  True
18507   19/04/2023 19:53:29 0 days 13:48:27  False
18507   19/04/2023 19:53:39 0 days 00:00:10  True
18507   19/04/2023 19:53:41 0 days 00:00:02  True

之后想对每个时间段块用.max() - .min()计算时长再求和,请问后续步骤是什么?有没有更优方法?


更新

  1. 通过创建判断边界解决问题:当时间间隔超过1小时标记为False。
def make_tof(df):
    df.TimeStamp = pd.to_datetime(df.TimeStamp)
    df.sort_values(by=['ID No.', 'TimeStamp'], inplace=True)
    df['diff'] = (df['TimeStamp'] - df['TimeStamp'].shift(1))
    # assumption: more than 1 hr
    df['diff_day'] = df['diff'].astype(str).str[:2]
    df['diff_truth1'] = df['diff'] < '01:00:00'
    df = df[df['diff_day'] != '-1']
    df.reset_index(drop=True, inplace=True)
    return df
  1. 为每个ID创建子DataFrame,通过字典分组后计算总和。
def cal_time(df):
    new_df = []

    df = make_tof(df)  # make decision boundary
    #  make df per ID
    lst_ID = df['ID No.'].unique()

    for j in lst_ID:
        df_ID = df[df['ID No.'] == j]
        d = {x: y for x, y in df_ID.loc[df_ID['diff_truth1'].ne(0)].groupby(df_ID['diff_truth1'].eq(0).cumsum())}

        for i in d:
            df3 = d[i].groupby(['ID No.'])['diff'].agg('sum')
            new_df.append(df3)
     
    a = pd.concat(new_df)
    a = pd.DataFrame(a)
    a = a.groupby(['ID No.'])['diff'].agg('sum')

该方法虽不够简洁,但能得到正确结果。


更简洁的实现方法

你的思路方向是对的,但可以利用pandas的分组能力简化代码,避免循环操作:

  1. 预处理时间列并排序:先将TimeStamp转为datetime类型,按ID和时间排序。
  2. 标记连续时间段:对每个ID,计算相邻时间差,当差超过1小时则标记为新块的起点,通过cumsum生成每个块的分组ID。
  3. 按ID和块分组计算时长:对每个块用max(TimeStamp) - min(TimeStamp)计算时长,最后按ID求和。

完整代码:

import pandas as pd

def calculate_total_duration(df):
    # 转换时间格式并排序
    df['TimeStamp'] = pd.to_datetime(df['TimeStamp'])
    df = df.sort_values(by=['ID No.', 'TimeStamp']).reset_index(drop=True)
    
    # 按ID分组,计算相邻时间差,标记新块
    df['is_new_block'] = df.groupby('ID No.')['TimeStamp'].diff().gt(pd.Timedelta(hours=1)).fillna(True)
    df['block_id'] = df.groupby('ID No.')['is_new_block'].cumsum()
    
    # 按ID和块分组,计算每个块的时长,再求和
    duration_per_block = df.groupby(['ID No.', 'block_id']).apply(
        lambda x: x['TimeStamp'].max() - x['TimeStamp'].min()
    )
    total_duration = duration_per_block.groupby('ID No.').sum()
    
    return total_duration

# 测试数据
df = pd.DataFrame({
    'ID No.': [18507]*27,
    'TimeStamp': [
        '19/04/2023 6:00:01', '19/04/2023 6:00:02', '19/04/2023 6:00:04',
        '19/04/2023 6:00:06', '19/04/2023 6:00:09', '19/04/2023 6:00:12',
        '19/04/2023 6:00:14', '19/04/2023 6:00:17', '19/04/2023 6:00:26',
        '19/04/2023 6:00:28', '19/04/2023 6:00:30', '19/04/2023 6:00:32',
        '19/04/2023 19:53:29', '19/04/2023 19:53:39', '19/04/2023 19:53:41',
        '19/04/2023 19:53:48', '19/04/2023 19:53:55', '19/04/2023 19:53:56',
        '19/04/2023 19:54:04', '19/04/2023 19:54:06', '19/04/2023 19:54:52',
        '20/04/2023 1:58:00', '20/04/2023 1:58:05', '20/04/2023 1:58:05',
        '20/04/2023 1:58:05', '20/04/2023 1:58:09', '20/04/2023 1:58:36',
        '20/04/2023 1:58:38', '20/04/2023 1:58:46', '20/04/2023 1:58:50'
    ]
})

result = calculate_total_duration(df)
print(result)

代码说明

  • is_new_block:标记每个ID下,当前行是否是新时间段的起点(时间差超过1小时或为第一行)。
  • block_id:通过cumsum为每个ID下的连续时间段生成唯一标识,同一连续块的行拥有相同的block_id。
  • 最后通过两次分组,先计算每个块的时长,再按ID汇总总时长,完全利用pandas的向量化操作,避免了循环,效率更高且代码更简洁。

内容的提问来源于stack exchange,提问作者riot12

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 20:23:11