将滚动功能应用于多个列的组合

时间:2017-05-29 03:42:58

标签: pandas group-by time-series

我想将滚动函数应用于由两列重复日期条目分组的数据框。具体来说,将“freq”和“window”作为日期时间值,而不仅仅是整数。

原则上,我尝试将How to apply rolling functions in a group by object in pandaspandas rolling sum of last five minutes中的方法结合起来。

输入

这是一个数据样本,其中一个id = 33,尽管我们期望有几个id。

X = [{'date': '2017-02-05', 'id': 33, 'item': 'A', 'points': 20},
 {'date': '2017-02-05', 'id': 33, 'item': 'B', 'points': 10},
 {'date': '2017-02-06', 'id': 33, 'item': 'B', 'points': 10},
 {'date': '2017-02-11', 'id': 33, 'item': 'A', 'points': 1},
 {'date': '2017-02-11', 'id': 33, 'item': 'A', 'points': 1},
 {'date': '2017-02-11', 'id': 33, 'item': 'A', 'points': 1},
 {'date': '2017-02-13', 'id': 33, 'item': 'A', 'points': 4}]

# df = pd.DataFrame(X) and reindex df to pd.to_datetime(df['date'])

df

            id item  points
date                       
2017-02-05  33    A      20
2017-02-05  33    B      10
2017-02-06  33    B      10
2017-02-11  33    A       1
2017-02-11  33    A       1
2017-02-11  33    A       1
2017-02-13  33    A       4

目标

每2天对每个'id'进行一次采样(freq ='2d')并返回前三天每个项目的总积分总和(window ='3D'),包括结束日期

所需输出

            id    A    B
date                       
2017-02-05  33    20   10
2017-02-07  33    20   30    
2017-02-09  33    0    10
2017-02-11  33    3    0
2017-02-13  33    7    0

E.g。在包含权利的结束日期2017-02-13中,我们将2017-02-11的3天期间样本计算到2017-02-13。在此期间,id = 33的A点的总和等于1 + 1 + 1 + 4 = 7

尝试

由于重复日期,尝试使用pd.rolling_sum的groupby不起作用

df.groupby(['id', 'item'])['points'].apply(pd.rolling_sum, freq='4D', window=3)
ValueError: cannot reindex from a duplicate axis

另请注意,文档http://pandas.pydata.org/pandas-docs/version/0.17.0/generated/pandas.rolling_apply.html'window'是一个表示样本周期大小的int,而不是样本的天数。

我们也可以尝试重新取样并使用最后一次,但是似乎没有使用3天所需的回顾

df.groupby(['id', 'item'])['points'].resample('2D', label='right', closed='right').\
apply(lambda x: x.last('3D').sum())

id  item  date      
33  A     2017-02-05    20
          2017-02-07     0
          2017-02-09     0
          2017-02-11     3
          2017-02-13     4
    B     2017-02-05    10
          2017-02-07    10

当然,在唯一id的ID上设置一个循环,选择df_id = df [df ['id'] == ID],并对这些句点求和确实有效,但计算密集,并且不利用groupby的好处向量化。

感谢@jezrael目前提出的好建议

备注

Pandas版本= 0.20.1

关于为什么滚动()的文档在这里我有点困惑:https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.rolling.html 建议“window”参数可以是int或offset但是在尝试df.rolling时(window ='3D',...)我得到raise ValueError("window must be an integer") 看来上面的文档与./core/window.py中滚动窗口的最新代码不一致: https://github.com/pandas-dev/pandas/blob/master/pandas/core/window.py

elif not is_integer(self.window):
            raise ValueError("window must be an integer")

2 个答案:

答案 0 :(得分:3)

  • 当我们拥有单一级别的日期时间索引时,最简单的方法是使用日期频率处理resamplerolling
  • 但是,我无法pivot / unstack在不处理重复A / B的情况下,{I} groupby和{{1} }}
  • sum一个级别unstack所以我可以date。目前,当我fill_value=0一次超过一个级别时,我无法fill_value=0。我用转置unstack
  • 来弥补它
  • 现在我已经在索引中获得了单个级别,我使用索引中的最小值到最大值的日期范围重新索引
  • 最后,我做了3天的滚动总和,每隔2天用T
  • 重新取样一次结果
  • 我通过一些重命名索引和一个支点来清理它。
resample

答案 1 :(得分:1)

df = pd.DataFrame(X) 

# group sum by day
df = df.groupby(['date', 'id', 'item'])['points'].sum().reset_index().sort_values(['date', 'id', 'item'])

# convert index to datetime index
df = df.set_index('date')
df.index = DatetimeIndex(df.index)

# rolloing sum by 3D
df['pointsum'] = df.groupby(['id', 'item']).transform(lambda x: x.rolling(window='3D').sum())

# reshape dataframe
df = df.reset_index().set_index(['date', 'id', 'item'])['pointsum'].unstack().reset_index().set_index('date').fillna(0)

df