假设我有一个DataFramedf=pd.DataFrame({'a':[1,2,np.nan,3,4,5,3], 'b':[11,22,22,11,22,22,22]})
a b
0 1.0 11
1 2.0 22
2 NaN 22
3 3.0 11
4 4.0 22
5 5.0 22
6 3.0 22
我想计算一个简化的数据帧,其中我按b
分组,其中我的列取决于分组平均值。具体来说,我希望该列包含
a
中小于分组平均值的元素数。
为此,我找到了一个似乎可以改进的解决方案,因为我猜它重新计算了"11"组的平均值2次和"22"组的5次:
使用groupby、agg和NamedAgg的慢速解决方案:
df.groupby('b').agg(c=pd.NamedAgg(column='a', aggfunc=lambda x: sum(i<x.mean() for i in x)))
dff=df.groupby('b').agg(c=pd.NamedAgg(column='a', aggfunc=lambda x: sum(i<x.mean() for i in x)))
print(dff)
c
b
11 1
22 2
你知道一种更好的方法吗,每组只计算一次平均值?
我已经在pandasmerge、concat、join、agg、apply等中搜索了参数。但我认为必须有这些参数的巧妙组合才能实现我想要做的事情。
不要使用python的sum
,使用矢量对应项,它将使您能够为每个组只计算一次平均值:
df.groupby('b')['a'].agg(c=lambda s: s.lt(s.mean()).sum())
输出:
c
b
11 1
22 2
速度比较
## provided example
# vectorial approach
1.07 ms ± 33.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# loop
2.86 ms ± 129 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
## 70k rows
# vectorial approach
3.19 ms ± 391 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# loop
7.67 s ± 104 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
根据我的经验,如果有一个大的数据集(数百万或更多的obs),那么使用merge
比使用lambda
或transform
更高效、更快。
tem = df.groupby('b')['a'].mean().reset_index(name='mean')
df = pd.merge(df, tem, on='b', how='left')
df.loc[df['a']<df['mean']].groupby('b')['a'].count().reset_index(name='c')