Pandas groupby具有滚动日期偏移的多个列 - 如何?

时间:2018-05-18 14:39:22

标签: python python-3.x pandas pandas-groupby

我正在尝试根据移动的2个工作日窗口对分区数据进行滚动求和。它感觉应该既简单又广泛使用,但解决方案超出了我的范围。

#generate sample data
import pandas as pd
import numpy as np
import datetime
vals = [-4,17,-4,-16,2,20,3,10,-17,-8,-21,2,0,-11,16,-24,-10,-21,5,12,14,9,-15,-15]
grp = ['X']*6 + ['Y'] * 6 + ['X']*6 + ['Y'] * 6
typ = ['foo']*12+['bar']*12
dat = ['19/01/18','19/01/18','22/01/18','22/01/18','23/01/18','24/01/18'] * 4
#create dataframe with sample data
df = pd.DataFrame({'group': grp,'type':typ,'value':vals,'date':dat})
df.date = pd.to_datetime(df.date)
df.head(12)

给出以下内容(注意这只是头12行):

    date    group   type    value
0   19/01/2018  X   foo     -4
1   19/01/2018  X   foo     17
2   22/01/2018  X   foo     -4
3   22/01/2018  X   foo     -16
4   23/01/2018  X   foo     2
5   24/01/2018  X   foo     20
6   19/01/2018  Y   foo     3
7   19/01/2018  Y   foo     10
8   22/01/2018  Y   foo     -17
9   22/01/2018  Y   foo     -8
10  23/01/2018  Y   foo     -21
11  24/01/2018  Y   foo     2

所需的结果是(此处显示的所有行):

    date    group   type    2BD Sum
1   19/01/2018  X   foo     13
2   22/01/2018  X   foo     -7
3   23/01/2018  X   foo     -18
4   24/01/2018  X   foo     22
5   19/01/2018  Y   foo     13
6   22/01/2018  Y   foo     -12
7   23/01/2018  Y   foo     -46
8   24/01/2018  Y   foo     -19
9   19/01/2018  X   bar     -11
10  22/01/2018  X   bar     -19
11  23/01/2018  X   bar     -18
12  24/01/2018  X   bar     -31
13  19/01/2018  Y   bar     17
14  22/01/2018  Y   bar     40
15  23/01/2018  Y   bar     8
16  24/01/2018  Y   bar     -30

我已查看此question并尝试了

df.groupby(['group','type']).rolling('2d',on='date').agg({'value':'sum'}
).reset_index().groupby(['group','type','date']).agg({'value':'sum'}).reset_index()

如果'值'哪个会正常工作?总是积极的,但这不是这里的情况。我已经尝试了许多导致错误的其他方法,如果它有价值,我可以列出。有人可以帮忙吗?

2 个答案:

答案 0 :(得分:0)

IIUC,从您的代码开始

import pandas as pd
import numpy as np
import datetime
vals = [-4,17,-4,-16,2,20,3,10,-17,-8,-21,2,0,-11,16,-24,-10,-21,5,12,14,9,-15,-15]
grp = ['X']*6 + ['Y'] * 6 + ['X']*6 + ['Y'] * 6
typ = ['foo']*12+['bar']*12
dat = ['19/01/18','19/01/18','22/01/18','22/01/18','23/01/18','24/01/18'] * 4
df = pd.DataFrame({'group': grp,'type':typ,'value':vals,'date':dat})
df.date = pd.to_datetime(df.date)

我们首先按grouptypedate进行分组,然后在每天内总结:

df2 = df.groupby(["group", "type", "date"]).sum().reset_index().sort_values("date")

现在,您只需使用rolling执行min_periods=1和(),这样您的第一个值就不是NaN。但是,你不会

k = df2.groupby(["group", "type"]).value.rolling(window=2, min_periods=1).sum()

这会产生

group  type    
X      bar   0    -11.0
             1    -19.0
             2    -18.0
             3    -31.0
       foo   4     13.0
             5     -7.0
             6    -18.0
             7     22.0
Y      bar   8     17.0
             9     40.0
             10     8.0
             11   -30.0
       foo   12    13.0
             13   -12.0
             14   -46.0
             15   -19.0

这已经是您想要的,但没有您的date值。为了得到日期,我们可以在这里做一个技巧,这只是改变你的这个多指数obj的第三级,你的date值在类似的df中以相同的方式分组。因此,我们可以做到

aux = df2.groupby(["group", "type", "date"]).date.rolling(2).count().index.get_level_values(2)

并替换索引:

k.index = pd.MultiIndex.from_tuples([(k.index[x][0], k.index[x][1], aux[x]) for x in range(len(k.index))])

最后,您有预期的输出:

k.to_frame()

    group   type    date        value
0   X       bar     2018-01-19  -11.0
1   X       bar     2018-01-22  -19.0
2   X       bar     2018-01-23  -18.0
3   X       bar     2018-01-24  -31.0
4   X       foo     2018-01-19  13.0
5   X       foo     2018-01-22  -7.0
6   X       foo     2018-01-23  -18.0
7   X       foo     2018-01-24  22.0
8   Y       bar     2018-01-19  17.0
9   Y       bar     2018-01-22  40.0
10  Y       bar     2018-01-23  8.0
11  Y       bar     2018-01-24  -30.0
12  Y       foo     2018-01-19  13.0
13  Y       foo     2018-01-22  -12.0
14  Y       foo     2018-01-23  -46.0
15  Y       foo     2018-01-24  -19.0

答案 1 :(得分:0)

我希望以下方法能起作用:

g = lambda ts: ts.rolling('2B', on='date')['value'].sum()
df.groupby(['group', 'type']).apply(g)

但是,由于工作日的频率不是固定的,因此出现错误。
这使我提出了以下丑陋的解决方案:

value_per_bday = lambda df: df.resample('B', on='date')['value'].sum()
df = df.groupby(['group', 'type']).apply(value_per_bday).stack()
value_2_bdays = lambda x: x.rolling(2, min_periods=1).sum()
df = df.groupby(axis=0, level=['group', 'type']).apply(value_2_bdays)

也许您可以选择使用某个功能听起来更好。

def resample_and_sum(x):
    x = x.resample('B', on='date')['value'].sum()
    x = x.rolling(2, min_periods=1).sum()
    return x

df = df.groupby(['group', 'type']).apply(resample_and_sum).stack()