Groupby一些列并计算列与另一列之间的最大差异

时间:2017-11-24 13:09:12

标签: pandas pandas-groupby

考虑以下数据框(请参阅此答案的结尾以获取生成代码的代码):

    A         T0         T1
0   0 2017-01-02 2017-01-04
1   2 2017-01-02 2017-01-05
2   1 2017-01-03 2017-01-04
3   3 2017-01-04 2017-01-07
4   2 2017-01-07 2017-01-10
5   0 2017-01-08 2017-01-10
6   3 2017-01-08 2017-01-09
7   1 2017-01-10 2017-01-11
8   0 2017-01-11 2017-01-13
9   3 2017-01-12 2017-01-15
10  2 2017-01-13 2017-01-16
11  1 2017-01-15 2017-01-17
12  0 2017-01-18 2017-01-20
13  3 2017-01-19 2017-01-20
14  1 2017-01-20 2017-01-22
15  2 2017-01-20 2017-01-21
16  2 2017-02-03 2017-02-06
17  1 2017-02-03 2017-02-06
18  0 2017-02-04 2017-02-07
19  3 2017-02-05 2017-02-07
20  1 2017-02-07 2017-02-08
21  3 2017-02-09 2017-02-11
22  0 2017-02-09 2017-02-10
23  1 2017-02-13 2017-02-16
24  3 2017-02-15 2017-02-17
25  2 2017-02-15 2017-02-18
26  0 2017-02-17 2017-02-18
27  2 2017-02-19 2017-02-21
28  3 2017-02-20 2017-02-21
29  2 2017-02-24 2017-02-27
30  1 2017-02-25 2017-02-26
31  0 2017-02-27 2017-03-01

我希望使用pandas.Grouper(例如按月)和A进行分组,并为每个组g计算g.T0 - g.T1.shift()的最大值。

我目前正在做以下事情:

def fun(x):
    x['G'] = x.T0 - x.T1.shift()
    return x

u = df.groupby((pd.Grouper(freq='MS', key='T0'), 'A')).apply(fun)
u = df.groupby((pd.Grouper(freq='MS', key='T0'), 'A'))['G'].max()

这有效,但是非常慢 - 我当前的数据帧包含~80m行,并且需要超过30分钟来计算我想要的组。

上述数据框的预期输出为:

T0          A
2017-01-01  0   5 days
            1   6 days
            2   4 days
            3   4 days
2017-02-01  0   9 days
            1   9 days
            2   9 days
            3   4 days
Name: G, dtype: timedelta64[ns]

我测试了另一种方法,包括在结束前计算差距:

df = df.sort_values(['A', 'T0'])
df['G'] = df.T0 - df.T1.shift()
df.loc[df['A'].diff() != 0, 'G'] = pd.NaT

然后我可以简单地说:

df.groupby((pd.Grouper(freq='MS', key='T0'), 'A'))['G'].max()

这里的问题是每组的第一个条目是当前组和前一组之间的差距,所以我得到:

T0          A
2017-01-01  0    5 days
            1    6 days
            2    4 days
            3    4 days
2017-02-01  0   15 days
            1   12 days
            2   13 days
            3   16 days
Name: G, dtype: timedelta64[ns]

我需要的是:

df.groupby((pd.Grouper(freq='MS', key='T0'), 'A')).all_but_first()['G'].max()

问题是pandas中不存在all_but_first

有没有办法:

  • 使用apply;
  • 加速第一个代码
  • 为每个组计算最大值,而不考虑每个组的第一个值?

生成数据帧的代码:

import numpy as np
import pandas as pd

A = np.repeat(range(4), 8)
T0 = [1, 7, 10, 17, 34, 39, 47, 57, 
      2, 9, 14, 19, 33, 37, 43, 55, 
      1, 6, 12, 19, 33, 45, 49, 54, 
      3, 7, 11, 18, 35, 39, 45, 50]
T1 = [3, 9, 12, 19, 37, 40, 48, 59, 
      3, 10, 16, 21, 36, 38, 46, 56, 
      4, 9, 15, 20, 36, 48, 51, 57, 
      6, 8, 14, 19, 37, 41, 47, 51]
df = pd.DataFrame({'A': A, 'T0': T0, 'T1': T1})
df['T0'] = pd.to_datetime(df['T0'], unit='D', origin=pd.Timestamp('2017-01-01'))
df['T1'] = pd.to_datetime(df['T1'], unit='D', origin=pd.Timestamp('2017-01-01'))
df = df.sort_values('T0')
df = df.reset_index(drop=True)

1 个答案:

答案 0 :(得分:2)

您可以使用:

df['new'] = df['T0'].sub(df.groupby((pd.Grouper(freq='MS', key='T0'), 'A'))['T1'].shift())
df = df.groupby((pd.Grouper(freq='MS', key='T0'), 'A'))['new'].max()
print (df)
T0          A
2017-01-01  0   5 days
            1   6 days
            2   4 days
            3   4 days
2017-02-01  0   9 days
            1   9 days
            2   9 days
            3   4 days
Name: new, dtype: timedelta64[ns]

一个想法是在max中使用apply

u = df.groupby('A').apply(lambda x: (x.T0 - x.T1.shift()).max())
print (u)
A
0    4.0
1    6.0
2    5.0
3    3.0
dtype: float64

或首先减去shifted列,然后汇总max

df = df['T0'].sub(df.groupby(['A'])['T1'].shift()).groupby(df['A']).max()
print (df)
A
0    4.0
1    6.0
2    5.0
3    3.0
dtype: float64

通过更改数据编辑:

df = df['T0'].sub(df.groupby(['A', 'V'])['T1'].shift()).groupby([df['A'], df['V']]).max()
print (df)
A  V    
0  False    4.0
   True     2.0
1  False    3.0
   True     4.0
2  False    1.0
   True     5.0
3  False    3.0
   True     3.0
dtype: float64