我需要在满足条件的1d NumPy数组或Pandas数值系列中找到第一个值的索引。数组很大,索引可能在数组的开始或结尾附近,或者可能根本不满足条件。我无法提前告诉您哪种可能性更大。如果不满足条件,则返回值为-1。我考虑了几种方法。
尝试1
# func(arr) returns a Boolean array
idx = next(iter(np.where(func(arr))[0]), -1)
但这通常太慢,因为func(arr)在整个数组上应用矢量化函数,而不是在满足条件时停止。具体来说,在数组开始附近满足条件时,这很昂贵。
尝试2
np.argmax是稍快,但无法确定何时条件永不满足:
np.random.seed(0)
arr = np.random.rand(10**7)
assert next(iter(np.where(arr > 0.999999)[0]), -1) == np.argmax(arr > 0.999999)
%timeit next(iter(np.where(arr > 0.999999)[0]), -1) # 21.2 ms
%timeit np.argmax(arr > 0.999999) # 17.7 ms
np.argmax(arr > 1.0)返回0,当条件,即一个实例并不满足。
尝试3
# func(arr) returns a Boolean scalar
idx = next((idx for idx, val in enumerate(arr) if func(arr)), -1)
但这在数组末尾附近满足条件时太慢了。大概是因为生成器表达式的大量__next__调用产生了昂贵的开销。
这是否总是一种折衷方案,或者对于通用而言func,是否有办法有效地提取第一个索引?
标杆管理
对于基准测试,假定func值大于给定常数时查找索引:
# Python 3.6.5, NumPy 1.14.3, Numba 0.38.0
import numpy as np
np.random.seed(0)
arr = np.random.rand(10**7)
m = 0.9
n = 0.999999
# Start of array benchmark
%timeit next(iter(np.where(arr > m)[0]), -1) # 43.5 ms
%timeit next((idx for idx, val in enumerate(arr) if val > m), -1) # 2.5 µs
# End of array benchmark
%timeit next(iter(np.where(arr > n)[0]), -1) # 21.4 ms
%timeit next((idx for idx, val in enumerate(arr) if val > n), -1) # 39.2 ms
胡说叔叔
Smart猫小萌