Быстрая проверка на NaN в NumPy - PullRequest
102 голосов
/ 18 июля 2011

Я ищу самый быстрый способ проверить наличие NaN (np.nan) в массиве NumPy X.np.isnan(X) не может быть и речи, поскольку он создает логический массив формы X.shape, который потенциально гигантский.

Я пробовал np.nan in X, но, похоже, это не сработает, поскольку np.nan != np.nan.Есть ли быстрый и эффективный способ памяти сделать это вообще?

(Для тех, кто спрашивает «как гигантский»: я не могу сказать. Это проверка ввода для кода библиотеки.)

Ответы [ 7 ]

140 голосов
/ 18 июля 2011

Решение Рэя хорошо. Тем не менее, на моей машине примерно в 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, чем в их отсутствие. Также кажется, что становится медленнее, когда NaN приближаются к началу массива. С другой стороны, пропускная способность 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
24 голосов
/ 18 июля 2011

Я думаю np.isnan(np.min(X)) должен делать то, что вы хотите.

17 голосов
/ 19 июля 2011

Даже если существует принятый ответ, я хотел бы продемонстрировать следующее (с 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(.), похоже, самый стабильный.

7 голосов
/ 10 июля 2017

Здесь есть два основных подхода:

  • Проверьте каждый элемент массива на nan и примите any.
  • Примените некоторую накопительную операцию, которая сохраняет nan s (как sum) и проверьте его результат.

Хотя первый подход, безусловно, самый чистый, тяжелая оптимизация некоторых накопительных операций (особенно тех, которые выполняются в BLAS, например * 1013)*) может сделать это довольно быстро.Обратите внимание, что 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)'
    )
2 голосов
/ 06 сентября 2017
enter code here
  1. использовать .any ()

if numpy.isnan(myarray).any()

  1. numpy.isfinite может быть лучше, чем isnan для проверки

if not np.isfinite(prop).all()

2 голосов
/ 02 декабря 2016

Если вас устраивает , это позволяет создать функцию быстрого короткого замыкания (останавливается, как только обнаружен 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 функция.

1 голос
/ 08 ноября 2013

С этим связан вопрос о том, как найти первое вхождение NaN. Это самый быстрый способ справиться с тем, что я знаю:

index = next((i for (i,n) in enumerate(iterable) if n!=n), None)
...