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小时标记为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
- 为每个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的分组能力简化代码,避免循环操作:
- 预处理时间列并排序:先将
TimeStamp转为datetime类型,按ID和时间排序。 - 标记连续时间段:对每个ID,计算相邻时间差,当差超过1小时则标记为新块的起点,通过
cumsum生成每个块的分组ID。 - 按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
相关产品推荐
相关产品推荐

