【问题标题】:Calculating time elapsed on a groupby object计算 groupby 对象经过的时间
【发布时间】:2015-11-15 07:10:06
【问题描述】:

我有一些时间序列数据,我想按类别分离出来,并在二进制值 == 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)分组,我不知道如何分组到单独的类别中。

感觉数据可能处于特别无用的状态,但我不知道如何使它变得更好 - 我尝试了一个将类别作为列的数据透视表,但由于 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

【问题讨论】:

  • 您给出的示例的期望结果是什么?
  • @unutbu 我已经添加了一个可能需要的输出,但就像我说的,我不确定数据形状是否特别有助于让我到达那里!

标签: python python-2.7 pandas time-series


【解决方案1】:

使用@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

【讨论】:

  • 我将二进制列放在一起而不考虑它的值,并将其应用于实际集合会引发进一步的复杂性:假设二进制打开一段时间,然后关闭,然后重新打开在一天之内,做 .last() - .first() 的差异不再是一个准确的答案。我已经更改了我的原始帖子以反映这一点(查看 2014-10-25 类别 c)
  • 将二进制标志添加到您的 grouper 中,或以较低的频率分组。不太确定您要做什么。
  • 对不起,如果我不清楚。本质上,二进制列告诉我开关已打开;我的数据库会定期更新(大约每分钟更新一次),我想看看它在 24 小时内完全打开了多长时间。因此,如果我查看特定日期的 a 类,我想选择开关打开的日期的时段 - 我的 utctime 仅指示发送消息的时间。目前,答案提供了一天中“第一个消息”和“最后一个消息”之间经过的时间,但忽略它们之间的任何“关闭”消息?
  • here部分末尾的配方;这称为部分分组。最好提出一个新问题。
【解决方案2】:

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

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

请注意,groupby 可以接受包含字符串和数组的列表。这 string 指的是列名,而数组充当虚拟的代理 柱子。 dates 不是 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]) 返回pd.Timedelta。 除以pd.Timedelta(1, 'm') 返回总分钟数。

请注意,使用 g.last()-g.first()Jeff's method 对于大型 DataFrames 可能要快得多,因为它在一个矢量化操作中计算所有 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

【讨论】:

  • 比 agg 要更惯用一点:(grouped.last()-grouped.first())/pd.Timedelta('1m'),虽然 YMMV 也可能更快。
  • 谢谢杰夫。事实上,这会更好——除非我遗漏了什么——grouped.last()grouped.first() DataFrames 只包含日期,而不是时间戳。因此,取这些日期的差异总是会导致 0。
  • 我添加了一个答案。无法分组并执行这种类型的计算(主要是因为分组对频率进行了分区),但只需添加一列即可解决问题。
猜你喜欢
  • 1970-01-01
  • 2016-04-25
  • 2020-12-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-26
  • 1970-01-01
  • 1970-01-01
  • 2010-11-15
相关资源
最近更新 更多