Skip to main content
 首页 » 编程设计

python之一组中的 Pandas 求和时间间隔,不包括重叠

2025年05月04日16开发

我有一个数据框,其中每一行都有开始时间和结束时间。每行也有一个他们所属的组。我想要一个新列,以秒为单位给出该组中所有时间的总和。

例如,如果我们有一个如下所示的组:

         id1:    |----| 
         id2:       |-----| 
         id3:                      |--------| 
                 .  .  .  .  .  .  .  .  .  .  . 
time ->        12:00    12:04    12:07    12:10 

然后对于属于该组的所有行将获得 4+3 分钟 = 420 秒的总时间

如果它们完全重叠,那么我们会得到这样的场景:

         id1:    |--------| 
         id2:    |--------| 
                 .  .  .  .  .  .  .  .  .  .  . 
time ->        12:00    12:04    12:07    12:10 

这将为我们提供 4 分钟 = 240 秒的结果。

下面是一些虚拟数据:


import pandas as pd 
 
ids = [x for x in range(10)] 
group = [0, 1, 1, 2, 2, 3, 4, 4, 4, 4] 
 
start = pd.to_datetime(["2019-10-21-16:20:00", "2019-10-21-16:22:00", "2019-10-21-16:22:00", "2019-10-21-16:15:00", 
         "2019-10-21-16:22:00", "2019-10-21-16:58:00", "2019-10-21-17:02:00", "2019-10-21-17:03:00", 
         "2019-10-21-17:04:00", "2019-10-21-17:20:00"]) 
 
end = pd.to_datetime(["2019-10-21-16:25:00", "2019-10-21-16:24:00", "2019-10-21-16:24:00", "2019-10-21-16:18:00", 
       "2019-10-21-16:26:00", "2019-10-21-17:02:00", "2019-10-21-17:06:00", "2019-10-21-17:07:00", 
       "2019-10-21-17:08:00", "2019-10-21-17:22:00"]) 
 
cols = ["id", "group", "start", "end"] 
 
 
df = pd.DataFrame(dict(zip(cols, [ids, group, start, end]))) 

到目前为止,我尝试过的方法显然是不正确的。我试过分组,然后找到该组每个开始和结束的最小值和最大值,然后将该间隔设置为总和。这种方法是不正确的,因为它还将包含间隔中的间隙。

gr = df.groupby("group").apply(lambda x : x.end.max() - x.start.min()) 
df['total_time'] = df.group.map(gr) 

请您参考如下方法:

首先,添加一个列来跟踪到目前为止看到的最晚结束时间(但只考虑同一组):

df['notbefore'] = df.groupby('group').end.shift().cummax() 

它移动了 1 位,以反射(reflect)在前几行中看到的最晚结束时间,不包括同一行。在 cummax() 之前使用 shift() 很重要,否则 shift 会在组之间“泄漏”值。

然后添加一个包含“有效”开始时间的列:

df['effstart'] = df[['start', 'notbefore']].max(1) 

这是经过修改的开始时间,因此它不早于任何之前的结束时间(以避免重叠)。

然后计算覆盖的总秒数:

df['effsec'] = (df.end - df.effstart).clip(np.timedelta64(0)) 

df 现在是:

   id  group               start                 end           notbefore            effstart   effsec 
0   0      0 2019-10-21 16:20:00 2019-10-21 16:25:00                 NaT 2019-10-21 16:20:00 00:05:00 
1   1      1 2019-10-21 16:22:00 2019-10-21 16:24:00                 NaT 2019-10-21 16:22:00 00:02:00 
2   2      1 2019-10-21 16:22:00 2019-10-21 16:24:00 2019-10-21 16:24:00 2019-10-21 16:24:00 00:00:00 
3   3      2 2019-10-21 16:15:00 2019-10-21 16:18:00                 NaT 2019-10-21 16:15:00 00:03:00 
4   4      2 2019-10-21 16:22:00 2019-10-21 16:26:00 2019-10-21 16:24:00 2019-10-21 16:24:00 00:02:00 
5   5      3 2019-10-21 16:58:00 2019-10-21 17:02:00                 NaT 2019-10-21 16:58:00 00:04:00 
6   6      4 2019-10-21 17:02:00 2019-10-21 17:06:00                 NaT 2019-10-21 17:02:00 00:04:00 
7   7      4 2019-10-21 17:03:00 2019-10-21 17:07:00 2019-10-21 17:06:00 2019-10-21 17:06:00 00:01:00 
8   8      4 2019-10-21 17:04:00 2019-10-21 17:08:00 2019-10-21 17:07:00 2019-10-21 17:07:00 00:01:00 
9   9      4 2019-10-21 17:20:00 2019-10-21 17:22:00 2019-10-21 17:08:00 2019-10-21 17:20:00 00:02:00 

获取最终结果:

df.groupby('group').effsec.sum() 

这给了你:

group 
0   00:05:00 
1   00:02:00 
2   00:05:00 
3   00:04:00 
4   00:08:00