对 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 数据框的索引查找。为何这么慢?如何加快速度? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

Neo4j的查询速度为何这么慢?这能商用吗

查找具有 NaN 值的 DataFrame 列表的索引 - Pandas

为啥 numpy 函数在 pandas 系列/数据帧上这么慢?

在 Pandas 数据框的多索引数据中按索引和值排序

Python Pandas - 查找两个数据帧之间的差异

阿里四面:为何MySQL没有使用建立的索引?