【发布时间】:2018-10-23 23:47:42
【问题描述】:
但是,关于计数两个或多个系列的组合的最佳方法的问题较少。提出了解决方案(1、2),但没有讨论何时以及为什么应该使用它们。
以下是对三种潜在方法的一些基准测试。我有两个具体问题:
- 为什么
grouper比count更高效?我预计count会更高效,因为它是在 C 中实现的。即使列数从 2 增加到 4,grouper的卓越性能仍然存在。 - 为什么
value_counter的表现比grouper差这么多?这是由于构建列表或从列表中构建系列的成本吗?
我知道输出是不同的,这也应该通知选择。例如,使用连续的numpy 数组与字典推导相比,按计数过滤更有效:
x, z = grouper(df), count(df)
%timeit x[x.values > 10] # 749µs
%timeit {k: v for k, v in z.items() if v > 10} # 9.37ms
但是,我的问题的重点是在系列与字典中构建可比较结果的性能。我的 C 知识有限,但如果有任何可以指出这些方法背后逻辑的答案,我将不胜感激。
基准代码
import pandas as pd
import numpy as np
from collections import Counter
np.random.seed(0)
m, n = 1000, 100000
df = pd.DataFrame({'A': np.random.randint(0, m, n),
'B': np.random.randint(0, m, n)})
def grouper(df):
return df.groupby(['A', 'B'], sort=False).size()
def value_counter(df):
return pd.Series(list(zip(df.A, df.B))).value_counts(sort=False)
def count(df):
return Counter(zip(df.A.values, df.B.values))
x = value_counter(df).to_dict()
y = grouper(df).to_dict()
z = count(df)
assert (x == y) & (y == z), "Dictionary mismatch!"
for m, n in [(100, 10000), (1000, 10000), (100, 100000), (1000, 100000)]:
df = pd.DataFrame({'A': np.random.randint(0, m, n),
'B': np.random.randint(0, m, n)})
print(m, n)
%timeit grouper(df)
%timeit value_counter(df)
%timeit count(df)
基准测试结果
在 python 3.6.2、pandas 0.20.3、numpy 1.13.1 上运行
机器规格:Windows 7 64 位,双核 2.5 GHz,4GB RAM。
键:g = grouper,v = value_counter,c = count。
m n g v c
100 10000 2.91 18.30 8.41
1000 10000 4.10 27.20 6.98[1]
100 100000 17.90 130.00 84.50
1000 100000 43.90 309.00 93.50
1 这不是错字。
【问题讨论】:
-
一个小侧边栏 -
pd.Series(list(zip(df.A, df.B))).value_counts(sort=False)改进了 一点 - 所以我假设除了list转换之外,排序会作为开销贡献 -
我一点也不惊讶为这个确切的用例量身定制的功能表现最好。
pandas比Counter更了解其数据结构。此外,pandas可能占用更少的内存,因为它知道如何重用其现有内存。 -
@BallpointBen,从哲学的角度来看,您的评论非常有道理。您能否参考源代码确定具体的根本原因(例如散列、迭代成本等)?
-
另外,对于
groupby的更高性能版本,将sort=False传递给groupby。 -
@Parfait,更新为 (a)
np.random.seed(0),(b) 更高版本的 Python / numpy / pandas + 包括机器规格,(c)sort=False用于pandas方法。跨度>
标签: python pandas dictionary dataframe counter