对 pandas 数据框的索引查找。为何这么慢?如何加快速度? [复制]
Posted
技术标签:
【中文标题】对 pandas 数据框的索引查找。为何这么慢?如何加快速度? [复制]【英文标题】:Indexed lookup on pandas dataframe. Why so slow? How to speed up? [duplicate] 【发布时间】:2019-06-26 14:36:37 【问题描述】:假设我有一个熊猫系列,我想将其用作多映射(每个索引键有多个值):
# intval -> data1
a = pd.Series(data=-np.arange(100000),
index=np.random.randint(0, 50000, 100000))
我想(尽快)从a
中选择所有值
其中a
的索引与另一个索引b
匹配。 (就像一个内部连接。或者一个合并,但对于系列)。
a
的索引中可能有重复项。
b
可能没有重复项,也不一定是a
索引的子集。为了给 pandas 最好的机会,我们假设 b
也可以作为排序索引对象提供:
b = pd.Index(np.unique(np.random.randint(30000, 100000, 100000))).sortvalues()
所以,我们会有类似的东西:
target
a b result
3 0 3 3 0
3 1 7 8 3
4 2 8 ...
8 3 ...
9 4
...
我也只对获取结果的值感兴趣(不需要索引[3,8,...]
)。
如果a
没有重复项,我们只需这样做:
a.reindex(b) # Cannot reindex a duplicate axis
因为&
维护了a
的副本,我们不能这样做:
d = a[a.index & b.index]
d = a.loc[a.index & b.index] # same
d = a.get(a.index & b.index) # same
print d.shape
所以我认为我们需要这样做:
common = (a.index & b.index).unique()
a.loc[common]
...这很麻烦,但也很慢。它不是建立要选择的项目列表很慢:
%timeit (a.index & b).unique()
# 100 loops, best of 3: 3.39 ms per loop
%timeit (a.index & b).unique().sort_values()
# 100 loops, best of 3: 4.19 ms per loop
...所以看起来它确实检索缓慢的值:
common = ((a.index & b).unique()).sort_values()
%timeit a.loc[common]
#10 loops, best of 3: 43.3 ms per loop
%timeit a.get(common)
#10 loops, best of 3: 42.1 ms per loop
...每秒大约 20 次操作。不完全是活泼的!为什么这么慢?
肯定有一种快速的方法可以从 pandas 数据框中查找一组值吗?我不想得到一个索引对象——实际上我所要求的只是对排序索引进行合并,或者(较慢)散列 int 查找。不管怎样,这应该是一个非常快的操作——而不是在我的 3Ghz 机器上每秒 20 个操作。
还有:
分析a.loc[common]
给:
ncalls tottime percall cumtime percall filename:lineno(function)
# All the time spent here.
40 1.01 0.02525 1.018 0.02546 ~:0(<method 'get_indexer_non_unique' indexing.py:1443(_has_valid_type)
...
# seems to be called a lot.
1500 0.000582 3.88e-07 0.000832 5.547e-07 ~:0(<isinstance>)
PS。我之前发布了一个类似的问题,关于为什么 Series.map 这么慢 Why is pandas.series.map so shockingly slow? 。原因是引擎盖下的惰性索引。这似乎没有发生在这里。
更新:
对于大小相似的 a 和常见的 a 是唯一的:
% timeit a.loc[common]
1000 loops, best of 3: 760 µs per loop
...正如@jpp 指出的那样。多索引可能是罪魁祸首。
【问题讨论】:
我给出了一半的答案,即“为什么慢”。至于“如何使其更快[使用非唯一索引]”,我认为这对于 Pandas 来说并非易事。请不要回答这个问题。 @coldspeed,我认为这个问题还有第二部分(即如何使用非唯一索引加速它),这就是为什么我建议 OP 不要接受我的答案。但也许这让它太宽泛了..不确定,但这是一个有效且具体的问题。 @jpp 我建议添加一个 cumcounted 索引以形成一个唯一的多索引。应该希望使它成为恒定的时间(因为索引是唯一的)。我会等待 OP 澄清(他们的第二部分不清楚)并将重新打开。 回复:重复的问题。我的问题是在不了解导致缓慢行为的原因的情况下提出的。它似乎相关,但不是引用问题的重复(这表明我们的非唯一索引在问题中很慢)。 + 我也在寻找如何解决这个问题(引用的问题没有)。回复,累计指数。听起来像是正确的解决方法——虽然不明显如何在不循环 Python 的情况下使用索引。不过会考虑的。 【参考方案1】:保证重复索引会减慢您的数据帧索引操作。您可以修改您的输入以向自己证明这一点:
a = pd.Series(data=-np.arange(100000), index=np.random.randint(0, 50000, 100000))
%timeit a.loc[common] # 34.1 ms
a = pd.Series(data=-np.arange(100000), index=np.arange(100000))
%timeit a.loc[common] # 6.86 ms
如this related question中所述:
当索引唯一时,pandas 使用哈希表将键映射到值 O(1)。 当索引是非唯一且已排序时,pandas 使用二进制搜索 O(logN), 当索引是随机排序时,pandas 需要检查索引中的所有键 索引 O(N)。
【讨论】:
嗯...很确定这些都可能是 O(1) 。 @user48956 你能详细说明一下吗? @MrR 你在问为什么我认为哈希查找可以是 O(1)?这只是计算机科学中的一个著名结果。无论索引是否排序,都可能实现 O(1) 查找成本。熊猫选择不这样做。 (悲伤的长号) 不,我不是在问这个。我想详细说明为什么你认为 pandas 在非唯一索引上选择二进制搜索作为默认值是不合理的。有哪些权衡?基于范围的操作浮现在脑海中。当您的索引大小超过哈希阈值大小时,还必须重建整个索引。当然,这些相同的考虑也可能适用于唯一索引,但也许这里有一些假设,即非唯一索引的用例会略有不同。或者,我们是说 pandas 开发人员无缘无故完全搞错了吗?以上是关于对 pandas 数据框的索引查找。为何这么慢?如何加快速度? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
查找具有 NaN 值的 DataFrame 列表的索引 - Pandas