73

我有一个grouped类型的时间序列对象<pandas.core.groupby.SeriesGroupBy object at 0x03F1A9F0>grouped.sum()给出了预期的结果,但我无法让 rolling_sum 使用该groupby对象。有没有办法将滚动功能应用于groupby对象?例如:

x = range(0, 6)
id = ['a', 'a', 'a', 'b', 'b', 'b']
df = DataFrame(zip(id, x), columns = ['id', 'x'])
df.groupby('id').sum()
id    x
a    3
b   12

但是,我想要类似的东西:

  id  x
0  a  0
1  a  1
2  a  3
3  b  3
4  b  7
5  b  12
4

5 回答 5

123

对于遇到这个老问题的 Google 员工:

关于@kekert 对@Garrett 使用新答案的评论

df.groupby('id')['x'].rolling(2).mean()

而不是现在弃用的

df.groupby('id')['x'].apply(pd.rolling_mean, 2, min_periods=1)

奇怪的是,新的 .rolling().mean() 方法似乎返回了一个多索引系列,首先由 group_by 列索引,然后是索引。然而,旧方法将简单地返回由原始 df 索引单独索引的系列,这可能不太有意义,但使得将该系列作为新列添加到原始数据框中非常方便。

所以我想我已经找到了一个使用新 rolling() 方法的解决方案,并且仍然可以正常工作:

df.groupby('id')['x'].rolling(2).mean().reset_index(0,drop=True)

这应该给你系列

0    0.0
1    0.5
2    1.5
3    3.0
4    3.5
5    4.5

您可以将其添加为列:

df['x'] = df.groupby('id')['x'].rolling(2).mean().reset_index(0,drop=True)
于 2016-12-16T19:31:54.603 回答
74

累计和

为了直接回答这个问题,cumsum 方法将产生所需的系列:

In [17]: df
Out[17]:
  id  x
0  a  0
1  a  1
2  a  2
3  b  3
4  b  4
5  b  5

In [18]: df.groupby('id').x.cumsum()
Out[18]:
0     0
1     1
2     3
3     3
4     7
5    12
Name: x, dtype: int64

每组熊猫滚动功能

更一般地说,任何滚动功能都可以按如下方式应用于每个组(使用@kekert 评论的新 .rolling 方法)。请注意,返回类型是多索引系列,这与以前(已弃用)的 pd.rolling_* 方法不同。

In [10]: df.groupby('id')['x'].rolling(2, min_periods=1).sum()
Out[10]:
id
a   0   0.00
    1   1.00
    2   3.00
b   3   3.00
    4   7.00
    5   9.00
Name: x, dtype: float64

要应用每组滚动功能并以原始数据帧顺序接收结果,应使用转换:

In [16]: df.groupby('id')['x'].transform(lambda s: s.rolling(2, min_periods=1).sum())
Out[16]:
0    0
1    1
2    3
3    3
4    7
5    9
Name: x, dtype: int64

不推荐使用的方法

作为参考,以下是现已弃用的 pandas.rolling_mean 的行为方式:

In [16]: df.groupby('id')['x'].apply(pd.rolling_mean, 2, min_periods=1)
Out[16]: 
0    0.0
1    0.5
2    1.5
3    3.0
4    3.5
5    4.5
于 2012-12-21T23:41:42.507 回答
10

这是另一种泛化良好并使用熊猫扩展方法的方法。

它非常高效,也非常适用于具有固定窗口的滚动窗口计算,例如时间序列。

# Import pandas library
import pandas as pd

# Prepare columns
x = range(0, 6)
id = ['a', 'a', 'a', 'b', 'b', 'b']

# Create dataframe from columns above
df = pd.DataFrame({'id':id, 'x':x})

# Calculate rolling sum with infinite window size (i.e. all rows in group) using "expanding"
df['rolling_sum'] = df.groupby('id')['x'].transform(lambda x: x.expanding().sum())

# Output as desired by original poster
print(df)
  id  x  rolling_sum
0  a  0            0
1  a  1            1
2  a  2            3
3  b  3            3
4  b  4            7
5  b  5           12
于 2018-09-27T19:22:02.147 回答
3

我不确定机制,但这有效。注意,返回的值只是一个 ndarray。我认为您可以以这种方式应用任何累积或“滚动”功能,它应该具有相同的结果。

我已经用它进行了测试cumprodcummax并且cummin它们都返回了一个 ndarray。我认为 pandas 足够聪明,可以知道这些函数返回一个系列,因此该函数被用作转换而不是聚合。

In [35]: df.groupby('id')['x'].cumsum()
Out[35]:
0     0
1     1
2     3
3     3
4     7
5    12

编辑:我很好奇这种语法确实返回了一个系列:

In [54]: df.groupby('id')['x'].transform('cumsum')
Out[54]:
0     0
1     1
2     3
3     3
4     7
5    12
Name: x
于 2012-12-21T23:07:35.800 回答
2

如果您需要将分组滚动功能重新分配回原始数据框,同时保持顺序和组,您可以使用该transform功能。

df.sort_values(by='date', inplace=True)
grpd = df.groupby('group_key')
#using center=false to assign values on window's last row
df['val_rolling_7_mean'] = grpd['val'].transform(lambda x: x.rolling(7, center=False).mean())
于 2020-10-01T07:02:36.910 回答