Сравнительный анализ производительности
ужно найти индекс первого значения в массиве 1d NumPy или числовом ряду Панд, удовлетворяющих условию. Массив большой, и индекс может быть ближе к началуили же конец массива,или же условие не может быть выполнено вообще. Я не могу сказать заранее, что более вероятно. Если условие не выполняется, возвращаемое значение должно быть-1
, Я рассмотрел несколько подходов.
# func(arr) returns a Boolean array
idx = next(iter(np.where(func(arr))[0]), -1)
Но это часто слишком медленноfunc(arr)
применяет векторизованную функцию квсе массив, а не остановка при выполнении условия. В частности, это дорого, когда условие выполняется вблизиНачало массива.
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
то есть случай, когда условиене довольный.
# 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