Быстрая проверка для NaN в NumPy
Я ищу самый быстрый способ проверить нахождение NaN (np.nan
) в массиве NumPy X
. np.isnan(X)
не может быть и речи, так как он создает логический массив формы X.shape
, который потенциально гигантский.
Я пробовал np.nan in X
, но это, похоже, не работает, потому что np.nan != np.nan
. Есть ли быстрый и эффективный с точки зрения памяти способ сделать это вообще?
(Тем, кто спросит "как гигантский": я не могу сказать. Это входная проверка для кода библиотеки.)
Ответы
Ответ 1
Раствор хорошо. Однако на моей машине примерно в 2,5 раза быстрее использовать numpy.sum
вместо numpy.min
:
In [13]: %timeit np.isnan(np.min(x))
1000 loops, best of 3: 244 us per loop
In [14]: %timeit np.isnan(np.sum(x))
10000 loops, best of 3: 97.3 us per loop
В отличие от min
, sum
не требует ветвления, что на современном оборудовании имеет тенденцию быть довольно дорогостоящим. Вероятно, это причина, по которой sum
работает быстрее.
edit Вышеуказанный тест был выполнен с одним NaN справа в середине массива.
Интересно отметить, что min
медленнее в присутствии NaN, чем в их отсутствие. Он также кажется медленнее, так как NaNs приближаются к началу массива. С другой стороны, пропускная способность sum
кажется постоянной независимо от того, существуют ли NaN и где они расположены:
In [40]: x = np.random.rand(100000)
In [41]: %timeit np.isnan(np.min(x))
10000 loops, best of 3: 153 us per loop
In [42]: %timeit np.isnan(np.sum(x))
10000 loops, best of 3: 95.9 us per loop
In [43]: x[50000] = np.nan
In [44]: %timeit np.isnan(np.min(x))
1000 loops, best of 3: 239 us per loop
In [45]: %timeit np.isnan(np.sum(x))
10000 loops, best of 3: 95.8 us per loop
In [46]: x[0] = np.nan
In [47]: %timeit np.isnan(np.min(x))
1000 loops, best of 3: 326 us per loop
In [48]: %timeit np.isnan(np.sum(x))
10000 loops, best of 3: 95.9 us per loop
Ответ 2
Я думаю, что np.isnan(np.min(X))
должен делать то, что вы хотите.
Ответ 3
Даже если есть приемлемый ответ, я хотел бы продемонстрировать следующее (с Python 2.7.2 и Numpy 1.6.0 на Vista):
In []: x= rand(1e5)
In []: %timeit isnan(x.min())
10000 loops, best of 3: 200 us per loop
In []: %timeit isnan(x.sum())
10000 loops, best of 3: 169 us per loop
In []: %timeit isnan(dot(x, x))
10000 loops, best of 3: 134 us per loop
In []: x[5e4]= NaN
In []: %timeit isnan(x.min())
100 loops, best of 3: 4.47 ms per loop
In []: %timeit isnan(x.sum())
100 loops, best of 3: 6.44 ms per loop
In []: %timeit isnan(dot(x, x))
10000 loops, best of 3: 138 us per loop
Таким образом, действительно эффективный способ может сильно зависеть от операционной системы. В любом случае dot(.)
, по-видимому, является наиболее стабильным.
Ответ 4
Здесь есть два основных подхода:
- Проверьте каждый элемент массива на
nan
и возьмите any
.
- Примените некоторую накопительную операцию, которая сохраняет
nan
(например, sum
), и проверьте ее результат.
Хотя первый подход, безусловно, является самым чистым, интенсивная оптимизация некоторых из кумулятивных операций (особенно тех, которые выполняются в BLAS, например, dot
), может сделать их довольно быстрыми. Обратите внимание, что dot
, как и некоторые другие операции BLAS, является многопоточным при определенных условиях. Это объясняет разницу в скорости между разными машинами.
![enter image description here]()
import numpy
import perfplot
def min(a):
return numpy.isnan(numpy.min(a))
def sum(a):
return numpy.isnan(numpy.sum(a))
def dot(a):
return numpy.isnan(numpy.dot(a, a))
def any(a):
return numpy.any(numpy.isnan(a))
def einsum(a):
return numpy.isnan(numpy.einsum("i->", a))
perfplot.show(
setup=lambda n: numpy.random.rand(n),
kernels=[min, sum, dot, any, einsum],
n_range=[2 ** k for k in range(20)],
logx=True,
logy=True,
xlabel="len(a)",
)
Ответ 5
Если вам удобно с numba он позволяет создать быстрое короткое замыкание (останавливается, как только обнаружено NaN):
import numba as nb
import math
@nb.njit
def anynan(array):
array = array.ravel()
for i in range(array.size):
if math.isnan(array[i]):
return True
return False
Если нет NaN
, функция может быть медленнее, чем np.min
, я думаю, что поскольку np.min
использует многопроцессорность для больших массивов:
import numpy as np
array = np.random.random(2000000)
%timeit anynan(array) # 100 loops, best of 3: 2.21 ms per loop
%timeit np.isnan(array.sum()) # 100 loops, best of 3: 4.45 ms per loop
%timeit np.isnan(array.min()) # 1000 loops, best of 3: 1.64 ms per loop
Но в случае, если в массиве есть NaN, особенно если он находится на низких индексах, то он намного быстрее:
array = np.random.random(2000000)
array[100] = np.nan
%timeit anynan(array) # 1000000 loops, best of 3: 1.93 µs per loop
%timeit np.isnan(array.sum()) # 100 loops, best of 3: 4.57 ms per loop
%timeit np.isnan(array.min()) # 1000 loops, best of 3: 1.65 ms per loop
Аналогичные результаты могут быть достигнуты с помощью Cython или расширения C, они немного сложнее (или легко доступны как bottleneck.anynan
) но ультимативно делать то же самое, что и моя функция anynan
.
Ответ 6
используйте .any()
if numpy.isnan(myarray).any()
numpy.isfinite может быть лучше, чем isnan для проверки
if not np.isfinite(prop).all()
Ответ 7
В связи с этим возникает вопрос, как найти первое вхождение NaN. Это самый быстрый способ справиться с тем, что я знаю:
index = next((i for (i,n) in enumerate(iterable) if n!=n), None)