计算在 groupby 对象上经过的时间

Calculating time elapsed on a groupby object

我有一些时间序列数据,我想按类别分离出来,并在二进制值 == 1 时进一步分离。我想计算二进制值 == 1 时每天经过的时间。

原始数据样本如下:

                       category  binary
utctime                                               
2014-10-23 13:15:08           a   0
2014-10-24 16:09:13           b   0
2014-10-24 18:56:01           a   1
2014-10-24 21:42:42           a   1
2014-10-25 00:29:22           a   0
2014-10-25 03:16:02           c   1
2014-10-25 06:02:43           c   1
2014-10-25 08:49:23           c   0
2014-10-25 11:36:03           c   1
2014-10-25 14:22:43           c   1
2014-10-25 17:09:24           d   0
2014-10-25 19:56:05           b   0
2014-10-25 22:42:45           b   0
2014-10-26 01:29:26           e   0
2014-10-26 04:16:15           d   0
2014-10-26 07:02:56           e   1
2014-10-26 09:49:36           e   1
2014-10-26 12:36:16           e   0
2014-10-26 15:22:57           e   0
2014-10-26 18:09:46           d   0
2014-10-26 20:56:26           b   0
2014-10-26 23:43:07           e   0

我开始过滤二进制列,然后按类别分组,但我丢失了日期索引。如果我按 index.date(或 pd.date_grouper)分组,我不知道如何将子分组到单独的类别中。

感觉数据的形状可能特别无用,但我不知道如何让它变得更好 - 我尝试了一个以类别为列的数据透视表 table,但由于 utctimes 是唯一的到类别,那没有用。我应该从索引中取出 utctime 吗?

所需的输出类似于以下内容:

category a   
date              total time binary == 1
2014-10-23          10 minutes
2014-10-24          5 minutes

category b   
date              total time binary == 1
2014-10-23          1 minutes
2014-10-24          15 minutes

要按类别和索引日期分组,您可以使用

date = df2.index.date
grouped = df2.groupby(['category', date])

请注意,groupby 可以接受同时包含字符串和数组的列表。这 字符串指的是列名,而数组充当虚拟 柱子。 date 不是 df2 的列,但您可以按它们分组。很酷吧?

要查找每个组中的总分钟数,您可以使用 lambda 函数进行汇总,例如

lambda x: (x.index[-1]-x.index[0])/pd.Timedelta(1, 'm')

(x.index[-1]-x.index[0]) 计算每组中第一个和最后一个时间戳之间的差异。请注意,这假定索引是按排序顺序排列的。 相差(x.index[-1]-x.index[0])returns一个pd.Timedelta。 除以 pd.Timedelta(1, 'm') returns 总分钟数。

请注意,使用 g.last()-g.first() 对于大型 DataFrame 可能要快得多,因为它在一个向量化操作中计算所有 Timedelta,而不是使用一个 lambda 函数调用计算每个 Timedelta对于每一行。


import numpy as np
import pandas as pd

df = pd.DataFrame(
    [['2014-10-23 13:15:08', 'a', 999.9, 0],
     ['2014-10-24 16:09:13', 'b', 24.1, 0],
     ['2014-10-24 18:56:01', 'a', 23.3, 1],
     ['2014-10-24 21:42:42', 'a', 23.0, 1],
     ['2014-10-25 00:29:22', 'a', 22.7, 0],
     ['2014-10-25 03:16:02', 'c', 23.1, 1],
     ['2014-10-25 06:02:43', 'c', 22.8, 1],
     ['2014-10-25 08:49:23', 'c', 23.7, 1],
     ['2014-10-25 11:36:03', 'c', 24.8, 0],
     ['2014-10-25 14:22:43', 'c', 25.7, 0],
     ['2014-10-25 17:09:24', 'd', 24.9, 0],
     ['2014-10-25 19:56:05', 'b', 24.6, 0],
     ['2014-10-25 22:42:45', 'b', 24.2, 0],
     ['2014-10-26 01:29:26', 'e', 22.7, 0],
     ['2014-10-26 04:16:15', 'd', 23.6, 0],
     ['2014-10-26 07:02:56', 'e', 22.4, 1],
     ['2014-10-26 09:49:36', 'e', 22.7, 1],
     ['2014-10-26 12:36:16', 'e', 22.2, 0],
     ['2014-10-26 15:22:57', 'e', 23.1, 0],
     ['2014-10-26 18:09:46', 'd', 23.8, 0],
     ['2014-10-26 20:56:26', 'b', 23.8, 0],
     ['2014-10-26 23:43:07', 'e', 22.7, 0]], 
    columns=['utctime', 'category', 'temp', 'binary'])
df = df.set_index('utctime')
df.index = pd.DatetimeIndex(df.index)
df2 = df.loc[df['binary']==1]
date = df2.index.date
grouped = df2.groupby(['category', date])
result = grouped['binary'].agg(
    lambda x: (x.index[-1]-x.index[0])/pd.Timedelta(1, 'm'))
print(result)

产量

category            
a         2014-10-24    166.683333
c         2014-10-25    333.350000
e         2014-10-26    166.666667
Name: binary, dtype: float64

使用@unutbu 数据和设置

添加一个我们也想比较的额外列

In [31]: df2['ts'] = df2.index

In [32]: df2
Out[32]: 
                    category  temp  binary                  ts
2014-10-24 18:56:01        a  23.3       1 2014-10-24 18:56:01
2014-10-24 21:42:42        a  23.0       1 2014-10-24 21:42:42
2014-10-25 03:16:02        c  23.1       1 2014-10-25 03:16:02
2014-10-25 06:02:43        c  22.8       1 2014-10-25 06:02:43
2014-10-25 08:49:23        c  23.7       1 2014-10-25 08:49:23
2014-10-26 07:02:56        e  22.4       1 2014-10-26 07:02:56
2014-10-26 09:49:36        e  22.7       1 2014-10-26 09:49:36

一种更通用的分组方式

In [33]: g = df2.groupby(['category',pd.Grouper(freq='D',level=0)])

虽然是 YMMV,但性能会更高。

In [34]: g.last()-g.first()
Out[34]: 
                     temp  binary       ts
category                                  
a        2014-10-24  -0.3       0 02:46:41
c        2014-10-25   0.6       0 05:33:21
e        2014-10-26   0.3       0 02:46:40

In [35]: result = g.last()-g.first()                   

In [46]: result['ts'] = result['ts'] / Timedelta('1m')

In [47]: result
Out[47]: 
                     temp  binary          ts
category                                     
a        2014-10-24  -0.3       0  166.683333
c        2014-10-25   0.6       0  333.350000
e        2014-10-26   0.3       0  166.666667