将滚动函数应用于多列分组

4
我想对一个数据框应用滚动函数,该数据框由两列分组,并具有重复的日期条目。具体来说,“freq”和“window”都是datetime值,而不仅仅是整数。
原则上,我试图结合如何在pandas中应用分组对象中的滚动函数pandas滚动最后五分钟的总和方法。 输入 以下是数据的示例,其中一个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

目标

每两天对每个'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

例如,在右包含的结束日期2017-02-13,我们对3天的时间段2017-02-11至2017-02-13进行采样。在此期间,id=33的总和A点数为1+1+1+4 = 7。
尝试使用pd.rolling_sum进行分组的尝试如下所示,由于日期重复而未能成功。
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'是一个表示样本周期大小的整数,而不是要采样的天数。

我们也可以尝试重新采样并使用last,但是所需的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 我有点困惑为什么这里rolling()的文档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")。似乎上述文档与rolling的最新代码./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个回答

3
  • 当我们有一个单层的日期时间索引时,处理resamplerolling与日期频率最容易。
  • 然而,如果不处理重复的A/B,我就无法适当地进行pivot/unstack,因此我使用groupbysum
  • 我将date的一个级别unstack,这样我就可以使用fill_value=0。目前,如果一次unstack超过一个级别,我就不能使用fill_value=0。我通过使用转置T来弥补这一点。
  • 现在,我在索引中获得了一个单层,我用从索引中最小到最大值的日期范围重新索引。
  • 最后,我进行滚动3天总和,并使用resample每2天对该结果进行采样。
  • 我通过重命名索引和另一个pivot来清理这个过程。
s = df.set_index(['id', 'item'], append=True).points
s = s.groupby(level=['date', 'id', 'item']).sum()

d = s.unstack('date', fill_value=0).T
tidx = pd.date_range(d.index.min(), d.index.max())
d = d.reindex(tidx, fill_value=0)

d1 = d.rolling('3D').sum().resample('2D').first().astype(d.dtypes).stack(0)
d1 = d1.rename_axis(['date', 'id']).rename_axis(None, 1)
print(d1)

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

感谢您的解释。在使用滚动方法(例如,在给定窗口中最近的非零条目的时间)访问日期时间值方面有什么技巧吗?目前,似乎只能在调用d.rolling('3D').apply(lambda X: func(X)).resample('2D')时访问浮点数组X。 - Quetzalcoatl
那听起来应该是一个单独的问题。提示是在将零掩码为np.nan后使用pd.Series.first_valid_indexpd.Series.last_valid_index。你也可以在numpy中使用一些其他技巧。 - piRSquared

2
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

这似乎没有涉及到“2D”采样频率? - Quetzalcoatl
可以在"x.rolling(window='3D')"中添加吗?由于示例数据太少,我无法理解整个问题。 - xmduhan
请参考piRsquared的答案 :-) - Quetzalcoatl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接