Обнаружение, если массив NumPy содержит хотя бы одно нечисловое значение? - программирование
Подтвердить что ты не робот

Обнаружение, если массив NumPy содержит хотя бы одно нечисловое значение?

Мне нужно написать функцию, которая будет определять, содержит ли вход хотя бы одно значение, которое не является числовым. Если обнаружено нечисловое значение, я подниму ошибку (потому что вычисление должно возвращать только числовое значение). Количество измерений входного массива неизвестно заранее - функция должна давать правильное значение независимо от ndim. В качестве дополнительного усложнения вход может быть одним поплавком или numpy.float64 или даже чем-то странным, как нулевой размерный массив.

Очевидным способом решения этой проблемы является запись рекурсивной функции, которая выполняет итерацию по каждому истребимому объекту в массиве до тех пор, пока не найдет не итерабельность. Он будет применять функцию numpy.isnan() для каждого неистребимого объекта. Если найдено хотя бы одно нечисловое значение, функция немедленно вернет False. В противном случае, если все значения в iterable являются числовыми, он в конечном итоге вернет True.

Это работает отлично, но это довольно медленно, и я ожидаю, что NumPy имеет гораздо лучший способ сделать это. Что такое альтернатива, которая быстрее и более numpyish?

Вот мой макет:

def contains_nan( myarray ):
    """
    @param myarray : An n-dimensional array or a single float
    @type myarray : numpy.ndarray, numpy.array, float
    @returns: bool
    Returns true if myarray is numeric or only contains numeric values.
    Returns false if at least one non-numeric value exists
    Not-A-Number is given by the numpy.isnan() function.
    """
    return True
4b9b3361

Ответ 1

Это должно быть быстрее, чем итерация, и будет работать независимо от формы.

numpy.isnan(myarray).any()

Изменить: 30 раз быстрее:

import timeit
s = 'import numpy;a = numpy.arange(10000.).reshape((100,100));a[10,10]=numpy.nan'
ms = [
    'numpy.isnan(a).any()',
    'any(numpy.isnan(x) for x in a.flatten())']
for m in ms:
    print "  %.2f s" % timeit.Timer(m, s).timeit(1000), m

Результаты:

  0.11 s numpy.isnan(a).any()
  3.75 s any(numpy.isnan(x) for x in a.flatten())

Бонус: он отлично работает для типов NumPy без массива:

>>> a = numpy.float64(42.)
>>> numpy.isnan(a).any()
False
>>> a = numpy.float64(numpy.nan)
>>> numpy.isnan(a).any()
True

Ответ 2

Если бесконечность - возможное значение, я бы использовал numpy.isfinite

numpy.isfinite(myarray).all()

Если приведенное выше значение равно True, то myarray не содержит значений numpy.nan, numpy.inf или -numpy.inf.

numpy.nan будет соответствовать значениям numpy.inf, например:

In [11]: import numpy as np

In [12]: b = np.array([[4, np.inf],[np.nan, -np.inf]])

In [13]: np.isnan(b)
Out[13]: 
array([[False, False],
       [ True, False]], dtype=bool)

In [14]: np.isfinite(b)
Out[14]: 
array([[ True, False],
       [False, False]], dtype=bool)

Ответ 3

С помощью numpy 1.3 или svn вы можете сделать это

In [1]: a = arange(10000.).reshape(100,100)

In [3]: isnan(a.max())
Out[3]: False

In [4]: a[50,50] = nan

In [5]: isnan(a.max())
Out[5]: True

In [6]: timeit isnan(a.max())
10000 loops, best of 3: 66.3 µs per loop

В сравнении с предыдущими версиями лечение сопоставлений с nans несовместимо.

Ответ 4

(np.where(np.isnan(A)))[0].shape[0] будет больше, чем 0, если A содержит хотя бы один элемент из nan, A может быть матрицей n x m.

Пример:

import numpy as np

A = np.array([1,2,4,np.nan])

if (np.where(np.isnan(A)))[0].shape[0]: 
    print "A contains nan"
else:
    print "A does not contain nan"

Ответ 5

Пфф! Микросекунды! Никогда не решайте проблему в микросекундах, которая может быть решена в наносекундах.

Обратите внимание, что принятый ответ:

  • перебирает все данные, независимо от того, найден ли нан
  • создает временный массив размера N, который является избыточным.

Лучшее решение - вернуть True немедленно, когда NAN найден:

import numba
import numpy as np

NAN = float("nan")

@numba.njit(nogil=True)
def _any_nans(a):
    for x in a:
        if np.isnan(x): return True
    return False

@numba.jit
def any_nans(a):
    if not a.dtype.kind=='f': return False
    return _any_nans(a.flat)

array1M = np.random.rand(1000000)
assert any_nans(array1M)==False
%timeit any_nans(array1M)  # 573us

array1M[0] = NAN
assert any_nans(array1M)==True
%timeit any_nans(array1M)  # 774ns  (!nanoseconds)

и работает для n-размеров:

array1M_nd = array1M.reshape((len(array1M)/2, 2))
assert any_nans(array1M_nd)==True
%timeit any_nans(array1M_nd)  # 774ns

Сравните это с простым решением:

def any_nans(a):
    if not a.dtype.kind=='f': return False
    return np.isnan(a).any()

array1M = np.random.rand(1000000)
assert any_nans(array1M)==False
%timeit any_nans(array1M)  # 456us

array1M[0] = NAN
assert any_nans(array1M)==True
%timeit any_nans(array1M)  # 470us

%timeit np.isnan(array1M).any()  # 532us

Метод раннего выхода - ускорение на 3 порядка или величины (в некоторых случаях). Не слишком потертый для простой аннотации.