Groupby 一些列并计算一列和另一列移位之间的最大差异
Groupby some columns and compute maximum difference between a column and another column shifted
考虑以下数据框(请参阅此答案的末尾以获取生成它的代码):
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()
问题是 all_but_first
在 pandas 中不存在。
有没有办法:
- 使用
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)
您可以使用:
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]
一个想法是在 apply
中使用 max
:
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
考虑以下数据框(请参阅此答案的末尾以获取生成它的代码):
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()
问题是 all_but_first
在 pandas 中不存在。
有没有办法:
- 使用
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)
您可以使用:
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]
一个想法是在 apply
中使用 max
:
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