【发布时间】: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循环的精美包装。第一行不是单个操作,也可能是非矢量化的(由于stracessor )。 -
[first_digit(x) for x in s]的速度应该非常相似。 -
除了 Quang Hoang 所说的,请注意“内置”行是用所有转换后的数字创建一个额外的中间字符串数组,而其他方法只需要创建最终数组并一次填充一个元素。无论如何,在许多情况下,NumPy 的字符串数组性能与常规 Python 代码并没有太大区别。另外,我猜这不是问题的重点,但是使用数字运算而不是转换为字符串并返回来提取第一个数字会更快。
-
stackoverflow.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。pandas和numpy都没有实现快速字符串代码;两者都严重依赖 Python。
标签: python pandas numpy vectorization