为啥 Pandas 应用可以比矢量化内置函数更快 [重复]

Posted

技术标签:

【中文标题】为啥 Pandas 应用可以比矢量化内置函数更快 [重复]【英文标题】:Why Pandas apply can be faster than vectorized built-ins [duplicate]为什么 Pandas 应用可以比矢量化内置函数更快 [重复] 【发布时间】:2020-12-15 15:46:40 【问题描述】:

我正在练习vectorization with Pandas,我发现了一个违反直觉的案例,即使用一系列内置矢量化方法比应用一个朴素的 Python 函数(提取所有数字的第一个数字)慢一个系列):

import sys
import numpy as np
import pandas as pd

s = pd.Series(np.arange(100_000))

def first_digit(x):
    return int(str(x)[0])

s.astype(np.str).str[0].astype(np.int) # 218ms "built-in"
s.apply(first_digit)                   # 104ms "apply"
s.map(first_digit)                     # 104ms "map"
np.vectorize(first_digit)(s)           #  78ms "vectorized"

所有 4 个实现都产生相同的 Pandas 系列,我完全理解 vectorized 函数调用可能比每个元素的 apply/map 更快。

但是,我很困惑为什么使用buil-in 方法会更慢...虽然我也对实际答案感兴趣,但我更感兴趣的是我必须学习的最小工具集是什么能够评估我对性能的假设

我的假设是,方法调用链正在创建 2 个额外的中间 Pandas Series,并且这些 Series 的值被贪婪地评估,导致 CPU 缓存未命中(必须从 RAM 加载中间 Series)。

按照该假设的步骤,我不知道如何证实或证伪:

    中间的 Series / numpy 数组是贪婪地评估还是懒惰地评估? 会导致 CPU 缓存未命中吗? 我还需要考虑哪些其他解释?

我的测量屏幕截图:

【问题讨论】:

最后三个都是for 循环的精美包装。第一行不是单个操作,也可能是非矢量化的(由于 str acessor )。 [first_digit(x) for x in s] 的速度应该非常相似。 除了 Quang Hoang 所说的,请注意“内置”行是用所有转换后的数字创建一个额外的中间字符串数组,而其他方法只需要创建最终数组并一次填充一个元素。无论如何,在许多情况下,NumPy 的字符串数组性能与常规 Python 代码并没有太大区别。另外,我猜这不是问题的重点,但是使用数字运算而不是转换为字符串并返回来提取第一个数字会更快。 ***.com/a/54028200/4333359。我认为这有一个很好的解释,尽管很长。 IMO,如果您正在执行某种字符串操作,事情会变得很慢,vectorization 与数字数组的含义不同。因此,如果可能,您应该尝试使用数学。在这个例子中。如果你做一些数学运算,你可以节省很多时间:(s//(10**(np.log10(s).clip(lower=0)//1))).astype(int)。 Ofc 假设您只有 [0, inf) 范围内的正整数。 s.to_numpy().astype('S1').astype(np.int) 大大缩短了时间(尽管np.vectorize 仍然更好)。在pandas 中,字符串存储在python 字符串的object dtype 中。所以astype(str)str[0] 操作都很慢。我的建议是移动动作 numpy 并利用它定义的长度字符串 dtypes。 pandasnumpy 都没有实现快速字符串代码;两者都严重依赖 Python。 【参考方案1】:

简而言之,你的问题是是否

s.astype(np.str).str[0].astype(np.int)

将您的操作融合在一起,然后迭代系列,或为每个操作创建一个临时系列,以及如何验证这一点?

我的假设(我猜你的假设)是后者。你有正确的解释,但如何测试?

我的建议是:

s1=s.astype(np.str)
s2=s1.str[0]
s3=s2.astype(np.int)

查看每个操作需要多长时间以及 3 个操作一起需要多长时间。很可能每个操作将花费大约相同的时间(每个操作的复杂性大致相同),这强烈表明我们的假设是正确的。如果前两个操作不需要时间,但几乎所有时间都是最后一个,那么我们的假设可能是错误的。

【讨论】:

谢谢,一个非常好的建议。事实上,.astype(np.int) 部分要快一个数量级 但是,评论中链接的 ***.com/a/54028200/1176601 让我的理解有了更大的改进(对于第 3 点),所以我将我的问题标记为重复

以上是关于为啥 Pandas 应用可以比矢量化内置函数更快 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

为什么内置函数abs()不能用于Python列表,但却能正确地用于NumPy数组和pandas数列(因为它会被向量化)?

为啥 dask 的“to_sql”比 pandas 花费更多时间?

为啥 `colMeans()` 和 `rowMeans()` 函数比使用带有 `lapply()` 的 mean 函数更快?

为啥矢量化更快

更快的 For 循环在 Pandas 中处理数据

Pandas:在每组中平均填充缺失值比变换更快