Pandas 上的 SQL“GROUP BY HAVING”相当于什么?

问题描述 投票:0回答:3

在 pandas 中使用 groupby 并并行应用过滤器的最有效方法是什么?

基本上我要求的是 SQL 中的等效项

select *
...
group by col_name
having condition

我认为有很多用例,从条件平均值、总和、条件概率等,这将使这样的命令非常强大。

我需要非常好的性能,所以理想情况下这样的命令不会是在 python 中完成多个分层操作的结果。

python pandas pandas-groupby
3个回答
101
投票

正如unutbu的评论中提到的,groupby的过滤器相当于SQL的HAVING:

In [11]: df = pd.DataFrame([[1, 2], [1, 3], [5, 6]], columns=['A', 'B'])

In [12]: df
Out[12]:
   A  B
0  1  2
1  1  3
2  5  6

In [13]: g = df.groupby('A')  #  GROUP BY A

In [14]: g.filter(lambda x: len(x) > 1)  #  HAVING COUNT(*) > 1
Out[14]:
   A  B
0  1  2
1  1  3

您可以编写更复杂的函数(这些函数适用于每个组),前提是它们返回一个简单的布尔值:

In [15]: g.filter(lambda x: x['B'].sum() == 5)
Out[15]:
   A  B
0  1  2
1  1  3

注意:可能存在一个错误,您无法编写函数来对用于分组的列进行操作...解决方法是手动对列进行分组,即

g = df.groupby(df['A']))


0
投票

我按 max 大于 20 的州和县进行分组,然后使用数据帧 loc 子查询 True 的结果值

counties=df.groupby(['state','county'])['field1'].max()>20
counties=counties.loc[counties.values==True]

0
投票

使用
groupby.transform
+ 布尔索引

尽管 pandas 中的等效语法是

groupby.filter
,但速度却慢得令人痛苦。如果性能很重要,那么最好是稍后执行 groupby 并过滤数据帧,而不是在 groupby 操作期间进行过滤。因为 groupby.filter 为每个组调用 Python 函数(例如 lambda),而
groupby.transform
在整个数据帧上调用 Cython 优化的函数,所以如果有很多组,后者会快得多。
使用 

groupby.transform

的要点是它返回一个与填充聚合值的原始数据帧具有相同索引的数据帧。由于它的输出具有相同的索引,因此可以用来过滤原始数据帧。

所以相当于

SELECT * FROM df GROUP BY colA HAVING COUNT(*) > 1

df[df.groupby('colA').transform('size') > 1]

以及相当于

SELECT * FROM df GROUP BY colA HAVING SUM(colB) > 5

df[df.groupby('colA')['colB'].transform('sum') > 5]

无论如何,正如下面的性能图所示,随着组数量的增加,
groupby.transform

+布尔索引的执行速度比

groupby.filter
快得多;例如,如果有 10k 组,则速度快 1000 倍。事实上,如果您的数据框有数百万个组,
groupby.filter
甚至可能无法运行,而
groupby.transform
+布尔索引将在合理的时间内完成工作。

用于生成上图的代码

import perfplot import pandas as pd import numpy as np def groupby_filter(df): g = df.groupby('A') return g.filter(lambda x: x['B'].sum() > 5) def groupby_transform(df): g = df.groupby('A') return df[g['B'].transform('sum') > 5] perfplot.plot( kernels=[groupby_filter, groupby_transform], n_range=[2**k for k in range(16)], setup=lambda n: pd.DataFrame({ 'A': np.random.choice(n, size=n, replace=False), 'B': np.random.randint(n, size=n)}), xlabel='Number of groups' )

© www.soinside.com 2019 - 2024. All rights reserved.