如何对 GroupBy 对象使用滚动函数
- 2025-03-10 08:47:00
- admin 原创
- 46
问题描述:
我有一个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
解决方案 1:
对于遇到这个老问题的 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)
解决方案 2:
累计数额
为了直接回答这个问题,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
pandas 每组滚动函数
更一般地,任何滚动函数都可以按如下方式应用于每个组(使用 @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
解决方案 3:
这是另一种具有很好概括性并使用了熊猫扩展方法的方法。
它非常高效,并且对于固定窗口的滚动窗口计算(例如时间序列)也非常有效。
# 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
解决方案 4:
如果您需要将分组滚动函数重新分配回原始 Dataframe,同时保持顺序和组,则可以使用该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())
解决方案 5:
我不确定机制,但这是可行的。请注意,返回的值只是一个 ndarray。我认为您可以以这种方式应用任何累积或“滚动”函数,它应该具有相同的结果。
我已经用和测试过它cumprod
,它们都返回一个 ndarray。我认为 pandas 足够聪明,知道这些函数返回一个系列,因此该函数被用作转换而不是聚合。cummax
`cummin`
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
扫码咨询,免费领取项目管理大礼包!