Chi*_*ony 4 python python-3.x pandas pandas-groupby
我有一个数据框,其中每一行都有一个开始时间和一个结束时间。每行也有一个他们所属的组。我想要一个新列,以秒为单位给出该组中所有时间的总和。
例如,如果我们有一个如下所示的组:
id1: |----|
id2: |-----|
id3: |--------|
. . . . . . . . . . .
time -> 12:00 12:04 12:07 12:10
Run Code Online (Sandbox Code Playgroud)
然后对于属于该组的所有行将获得 4+3 分钟 = 420 秒的总和时间
如果它们完全重叠,那么我们会得到这样的场景:
id1: |--------|
id2: |--------|
. . . . . . . . . . .
time -> 12:00 12:04 12:07 12:10
Run Code Online (Sandbox Code Playgroud)
这会给我们结果 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])))
Run Code Online (Sandbox Code Playgroud)
到目前为止,我尝试过的方法显然是不正确的。我试过分组,然后找到该组的每个开始和结束的最小值和最大值,然后将该间隔设置为总和。这种方法是不正确的,因为它还会包括间隔中的间隙。
gr = df.groupby("group").apply(lambda x : x.end.max() - x.start.min())
df['total_time'] = df.group.map(gr)
Run Code Online (Sandbox Code Playgroud)
首先,添加一列跟踪迄今为止看到的最新结束时间(但仅考虑同一组):
df['notbefore'] = df.groupby('group').end.shift().cummax()
Run Code Online (Sandbox Code Playgroud)
它移动了 1 以反映在前一行中看到的最晚结束时间,不包括同一行。拥有shift()before很重要cummax(),否则转移会在组之间“泄漏”值。
然后添加一个包含“有效”开始时间的列:
df['effstart'] = df[['start', 'notbefore']].max(1)
Run Code Online (Sandbox Code Playgroud)
这是修改后的开始时间,使其不在任何先前的结束时间之前(以避免重叠)。
然后计算涵盖的总秒数:
df['effsec'] = (df.end - df.effstart).clip(np.timedelta64(0))
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
要获得最终结果:
df.groupby('group').effsec.sum()
Run Code Online (Sandbox Code Playgroud)
这给了你:
group
0 00:05:00
1 00:02:00
2 00:05:00
3 00:04:00
4 00:08:00
Run Code Online (Sandbox Code Playgroud)