Groupby 值对数据框 pandas 的计数
Posted
技术标签:
【中文标题】Groupby 值对数据框 pandas 的计数【英文标题】:Groupby value counts on the dataframe pandas 【发布时间】:2017-01-01 03:29:52 【问题描述】:我有以下数据框:
df = pd.DataFrame([
(1, 1, 'term1'),
(1, 2, 'term2'),
(1, 1, 'term1'),
(1, 1, 'term2'),
(2, 2, 'term3'),
(2, 3, 'term1'),
(2, 2, 'term1')
], columns=['id', 'group', 'term'])
我想按id
和group
对其进行分组,并计算此id、组对的每个术语的数量。
所以最后我会得到这样的东西:
我能够通过使用df.iterrows()
遍历所有行并创建一个新数据框来实现我想要的,但这显然效率低下。 (如果有帮助,我事先知道所有术语的列表,其中大约有 10 个)。
看起来我必须先分组,然后计算值,所以我尝试了 df.groupby(['id', 'group']).value_counts()
,但它不起作用,因为 value_counts 在 groupby 系列而不是数据帧上运行。
无论如何我可以在不循环的情况下实现这一点?
【问题讨论】:
【参考方案1】:我使用groupby
和size
df.groupby(['id', 'group', 'term']).size().unstack(fill_value=0)
时间
1,000,000 行
df = pd.DataFrame(dict(id=np.random.choice(100, 1000000),
group=np.random.choice(20, 1000000),
term=np.random.choice(10, 1000000)))
【讨论】:
@jezrael thx,size
也更快。 crosstab
效率低得奇怪
我很惊讶crosstab
如此懒惰;)
@jezrael, crosstab
在内部使用 pivot_table
... ;)
@piRSquared - 你可以添加到计时df.groupby(['id', 'group', 'term'])['term'].size().unstack(fill_value=0)
吗?对我来说似乎更快。谢谢。
@piRSquared - 我在更大的 df 中尝试它并且更快一点(0.2ms,也许它是相同的;))【参考方案2】:
使用pivot_table()方法:
In [22]: df.pivot_table(index=['id','group'], columns='term', aggfunc='size', fill_value=0)
Out[22]:
term term1 term2 term3
id group
1 1 2 1 0
2 0 1 0
2 2 1 0 1
3 1 0 0
针对 700K 行 DF 计时:
In [24]: df = pd.concat([df] * 10**5, ignore_index=True)
In [25]: df.shape
Out[25]: (700000, 3)
In [3]: %timeit df.groupby(['id', 'group', 'term'])['term'].size().unstack(fill_value=0)
1 loop, best of 3: 226 ms per loop
In [4]: %timeit df.pivot_table(index=['id','group'], columns='term', aggfunc='size', fill_value=0)
1 loop, best of 3: 236 ms per loop
In [5]: %timeit pd.crosstab([df.id, df.group], df.term)
1 loop, best of 3: 355 ms per loop
In [6]: %timeit df.groupby(['id','group','term'])['term'].size().unstack().fillna(0).astype(int)
1 loop, best of 3: 232 ms per loop
In [7]: %timeit df.groupby(['id', 'group', 'term']).size().unstack(fill_value=0)
1 loop, best of 3: 231 ms per loop
针对 7M 行 DF 的计时:
In [9]: df = pd.concat([df] * 10, ignore_index=True)
In [10]: df.shape
Out[10]: (7000000, 3)
In [11]: %timeit df.groupby(['id', 'group', 'term'])['term'].size().unstack(fill_value=0)
1 loop, best of 3: 2.27 s per loop
In [12]: %timeit df.pivot_table(index=['id','group'], columns='term', aggfunc='size', fill_value=0)
1 loop, best of 3: 2.3 s per loop
In [13]: %timeit pd.crosstab([df.id, df.group], df.term)
1 loop, best of 3: 3.37 s per loop
In [14]: %timeit df.groupby(['id','group','term'])['term'].size().unstack().fillna(0).astype(int)
1 loop, best of 3: 2.28 s per loop
In [15]: %timeit df.groupby(['id', 'group', 'term']).size().unstack(fill_value=0)
1 loop, best of 3: 1.89 s per loop
【讨论】:
我只是想用更大的样本更新时间:-) 哇!在更大的范围内,pivot 似乎同样有效。我必须记住这一点。我会给你 +1,但我前段时间已经这样做了。 所以size
是我们忘记的别名here :)
@ayhan,很奇怪 - 这次df.assign(ones = np.ones(len(df))).pivot_table(index=['id','group'], columns='term', values = 'ones', aggfunc=np.sum, fill_value=0)
的解决方案有点慢 - 1 loop, best of 3: 2.55 s per loop
我认为这是因为您在那里使用了len
,而不是“大小”。 len
是一个 Python 函数,但我们作为字符串传递的函数是优化的 C 函数的别名。【参考方案3】:
与其记住冗长的解决方案,不如使用 pandas 为您内置的解决方案:
df.groupby(['id', 'group', 'term']).count()
【讨论】:
【参考方案4】:你可以使用crosstab
:
print (pd.crosstab([df.id, df.group], df.term))
term term1 term2 term3
id group
1 1 2 1 0
2 0 1 0
2 2 1 0 1
3 1 0 0
另一个解决方案是groupby
聚合size
,通过unstack
重塑:
df.groupby(['id', 'group', 'term'])['term'].size().unstack(fill_value=0)
term term1 term2 term3
id group
1 1 2 1 0
2 0 1 0
2 2 1 0 1
3 1 0 0
时间安排:
df = pd.concat([df]*10000).reset_index(drop=True)
In [48]: %timeit (df.groupby(['id', 'group', 'term']).size().unstack(fill_value=0))
100 loops, best of 3: 12.4 ms per loop
In [49]: %timeit (df.groupby(['id', 'group', 'term'])['term'].size().unstack(fill_value=0))
100 loops, best of 3: 12.2 ms per loop
【讨论】:
哇哇哇,你太棒了。你只用了 3 分钟(与我写循环所用的时间相同,而我写这个问题所用的时间更少)。如果你能写一些解释为什么会这样,我真的很感激,但很可能我能在几分钟内自己理解。 在你的情况下crosstab
比pivot_table
更好,因为默认聚合函数是len
(它与size
相同),我认为它也是更快的解决方案。 Crosstab
使用第一个参数作为列的 index
和 second
。给我一点时间,我试试添加时间。
但我认为最好在docs
中解释。【参考方案5】:
如果你想使用value_counts
,你可以在给定的系列上使用它,并采用以下方法:
df.groupby(["id", "group"])["term"].value_counts().unstack(fill_value=0)
或以等效方式,使用.agg
方法:
df.groupby(["id", "group"]).agg("term": "value_counts").unstack(fill_value=0)
另一种选择是直接在 DataFrame 本身上使用value_counts
,而不使用groupby
:
df.value_counts().unstack(fill_value=0)
【讨论】:
以上是关于Groupby 值对数据框 pandas 的计数的主要内容,如果未能解决你的问题,请参考以下文章
如何在 pandas 数据框中从 groupby 的结果生成所有值对
使用 pandas 在数据帧上执行 groupby,按计数排序并获取 python 中的前 2 个计数