Pandas v1.1.0:Groupby 滚动计数比滚动平均值和总和慢

nrc*_*001 18 python pandas

我正在使用 Pandas v1.1.0 通过滚动计数、求和和均值运行 group,我注意到滚动计数比滚动均值和总和慢得多。这似乎违反直觉,因为我们可以从平均值和总和中得出计数并节省时间。这是一个错误还是我错过了什么?多谢指教。

import pandas as pd

# Generate sample df
df = pd.DataFrame({'column1': range(600), 'group': 5*['l'+str(i) for i in range(120)]})

# sort by group for easy/efficient joining of new columns to df
df=df.sort_values('group',kind='mergesort').reset_index(drop=True)

# timing of groupby rolling count, sum and mean
%timeit df['mean']=df.groupby('group').rolling(3,min_periods=1)['column1'].mean().values
%timeit df['sum']=df.groupby('group').rolling(3,min_periods=1)['column1'].sum().values
%timeit df['count']=df.groupby('group').rolling(3,min_periods=1)['column1'].count().values

### Output
6.14 ms ± 812 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
5.61 ms ± 179 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
76.1 ms ± 4.78 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

### df Output for illustration
print(df.head(10))

   column1 group   mean     sum  count
0        0    l0    0.0     0.0    1.0
1      120    l0   60.0   120.0    2.0
2      240    l0  120.0   360.0    3.0
3      360    l0  240.0   720.0    3.0
4      480    l0  360.0  1080.0    3.0
5        1    l1    1.0     1.0    1.0
6      121    l1   61.0   122.0    2.0
7      241    l1  121.0   363.0    3.0
8      361    l1  241.0   723.0    3.0
9      481    l1  361.0  1083.0    3.0
Run Code Online (Sandbox Code Playgroud)

Pie*_*e D 1

您真的是指count(非 NaN 值的数量)吗?这不能仅从sum和推断出来mean

我怀疑您正在寻找的是一个size运算符(只是组的长度,无论是否存在任何 NaN)。虽然size存在于常规中groupby,但似乎在RollingGroupBy(至少从 pandas 1.1.4 开始)中不存在。可以通过以下方式计算滚动组的大小:

# DRY:
rgb = df.groupby('group').rolling(3, min_periods=1)['column1']

# size is either:
rgb.apply(len)

# or
rgb.apply(lambda g: g.shape[0])
Run Code Online (Sandbox Code Playgroud)

当然,这两者都没有那么快,因为需要调用每个组的函数,而不是全部矢量化并仅在滚动窗口索引start和 的基础上工作endrgb.sum()在我的系统上,上述任何一个都比或慢 2 倍rgb.mean()

考虑如何实现size:这是显而易见的(仅end - start针对每个窗口)。

现在,如果确实想加快速度count(非 NaN 值的计数):可以首先建立一个“累积计数”:

cumcnt = (1 - df['column1'].isnull()).cumsum()
Run Code Online (Sandbox Code Playgroud)

(这非常快,比rgb.mean()我的系统快大约 200 倍)。

那么滚动函数就可以简单地采用cumcnt[end] - cumcnt[start].

RollingGroupBy我对内部结构(以及它们对各种s的使用)了解不够,无法mixin评估可行性,但至少在功能上它看起来相当简单。

更新:

这些提交似乎已经解决了这个问题。这既快速又简单——熊猫的内部结构以及它们瑞士军刀上已有的所有工具给我留下了深刻的印象!