【问题标题】:Calculate rolling time difference in pandas efficiently有效计算熊猫的滚动时间差
【发布时间】:2014-05-22 00:58:18
【问题描述】:

我在 pandas 中有一个面板,正在尝试计算个人在每个阶段花费的时间。为了更好地理解这一点,我的数据集如下:

group       date    stage  
 A     2014-01-01   one   
 A     2014-01-03   one    
 A     2014-01-04   one    
 A     2014-01-05   two    
 B     2014-01-02  four    
 B     2014-01-06  five    
 B     2014-01-10  five    
 C     2014-01-03   two    
 C     2014-01-05   two    

我正在寻找计算阶段持续时间以提供:

 group       date    stage  dur
  A     2014-01-01   one    0
  A     2014-01-03   one    2
  A     2014-01-04   one    3
  A     2014-01-05   two    0
  B     2014-01-02  four    0
  B     2014-01-06  five    0
  B     2014-01-10  five    4
  C     2014-01-03   two    0
  C     2014-01-05   two    2

我在下面使用的方法非常慢。关于更快的方法有什么想法吗?

df['stage_duration'] = df.groupby(['group', 'stage']).date.apply(lambda y: (y - y.iloc[0])).apply(lambda y:y / np.timedelta64(1, 'D')))

【问题讨论】:

标签: python pandas


【解决方案1】:

根据您的代码(您的groupby/apply),看起来(尽管您的示例......但也许我误解了您想要什么,然后安迪做了什么是最好的主意)您正在处理“约会” ' 列在您的实际数据中是 datetime64 dtype 而不是 integer dtype。此外,您似乎想要计算从给定group/stage 的第一次观察中测量的天数变化。我认为这是一组更好的示例数据(如果我正确理解您的目标):

>>> df

  group       date stage  dur
0     A 2014-01-01   one    0
1     A 2014-01-03   one    2
2     A 2014-01-04   one    3
3     A 2014-01-05   two    0
4     B 2014-01-02  four    0
5     B 2014-01-06  five    0
6     B 2014-01-10  five    4
7     C 2014-01-03   two    0
8     C 2014-01-05   two    2

鉴于您应该通过在应用后以矢量化方式除以timedelta64 来修改您的应用(正如 Jeff 在他的评论中建议的那样)来加快速度(或者您可以在应用中进行) :

>>> df['dur'] = df.groupby(['group','stage']).date.apply(lambda x: x - x.iloc[0])
>>> df['dur'] /= np.timedelta64(1,'D')
>>> df

  group       date stage  dur
0     A 2014-01-01   one    0
1     A 2014-01-03   one    2
2     A 2014-01-04   one    3
3     A 2014-01-05   two    0
4     B 2014-01-02  four    0
5     B 2014-01-06  five    0
6     B 2014-01-10  five    4
7     C 2014-01-03   two    0
8     C 2014-01-05   two    2

但您也可以避免使用groupby/apply,因为您的数据按组、阶段、日期顺序排列。每个['group','stage'] 分组的第一个日期发生在组更改或阶段更改时。所以我认为您可以执行以下操作:

>>> beg = (df.group != df.group.shift(1)) | (df.stage != df.stage.shift(1))
>>> df['dur'] = (df['date'] - df['date'].where(beg).ffill())/np.timedelta64(1,'D')
>>> df

  group       date stage  dur
0     A 2014-01-01   one    0
1     A 2014-01-03   one    2
2     A 2014-01-04   one    3
3     A 2014-01-05   two    0
4     B 2014-01-02  four    0
5     B 2014-01-06  five    0
6     B 2014-01-10  five    4
7     C 2014-01-03   two    0
8     C 2014-01-05   two    2

说明:注意df['date'].where(beg) 创建的内容:

>>> beg = (df.group != df.group.shift(1)) | (df.stage != df.stage.shift(1))
>>> df['date'].where(beg)

0   2014-01-01
1          NaT
2          NaT
3   2014-01-05
4   2014-01-02
5   2014-01-06
6          NaT
7   2014-01-03
8          NaT

然后我 ffill 这些值并使用“日期”列取差值。

编辑:正如安迪指出的,你也可以使用transform

>>> df['dur'] = df.date - df.groupby(['group','stage']).date.transform(lambda x: x.iloc[0])
>>> df['dur'] /= np.timedelta64(1,'D')

  group       date stage  dur
0     A 2014-01-01   one    0
1     A 2014-01-03   one    2
2     A 2014-01-04   one    3
3     A 2014-01-05   two    0
4     B 2014-01-02  four    0
5     B 2014-01-06  five    0
6     B 2014-01-10  five    4
7     C 2014-01-03   two    0
8     C 2014-01-05   two    2

速度:我使用具有 400,000 次观察的相似数据框对这两种方法进行计时:

申请方法:

1 loops, best of 3: 18.3 s per loop

非申请方法:

1 loops, best of 3: 1.64 s per loop

所以我认为避免应用可以显着加快速度

【讨论】:

  • +1 这对于 OP 想要的东西可能更有意义......我认为您可以使用转换更有效地做到这一点。
  • 是的@Andy,我考虑过transform,但至少对于0.13.1,我通常发现转换不比通用apply快,所以我没有包括它。但我会用它来更新答案。
  • 有兴趣看看它是否更快,我的猜测是它会是(虽然将取决于组大小 - 如果更大的组怀疑会更快)。
  • @Andy,我得到transform 的速度要慢得多。我以为 Jeff 提到了 transform 的一些性能问题,但也许我记错了。
  • oop,是的,您的方法要快得多(专门针对这种连续情况),误读了您的第二个解决方案!
【解决方案2】:

我想我会在这里使用diff

In [11]: df.groupby('stage')['date'].diff().fillna(0)
Out[11]:
0    0
1    2
2    0
3    0
4    0
5    4
dtype: float64

(假设阶段是连续的。)

如果您只是减去每组中的第一个,请使用transform

In [21]: df['date'] - df.groupby('stage')['date'].transform(lambda x: x.iloc[0])
Out[21]:
0    0
1    2
2    0
3    0
4    0
5    4
Name: date, dtype: int64

注意:这可能要快得多...

【讨论】:

    猜你喜欢
    • 2019-11-19
    • 1970-01-01
    • 2013-06-23
    • 1970-01-01
    • 2018-03-10
    • 2015-01-12
    • 2016-01-20
    • 1970-01-01
    • 2017-04-06
    相关资源
    最近更新 更多