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

103

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

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

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

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

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
Салим Фадли
источник
3
Ваше описание contains_nanвыглядит подозрительно: «Возвращает false, если существует хотя бы одно нечисловое значение». Я ожидал, contains_nanчто вернусь, Trueесли массив содержит NaN.
Самуэль Тардье,
А как насчет таких входов, как array(['None', 'None'], dtype=object)? Должен ли такой ввод просто вызывать исключение?
Финн Оруп Нильсен 08
НЕ используйте float('nan') in x. Это не работает.
Чарли Паркер,

Ответы:

184

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

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
Павел
источник
1
с numpy 1.7 версия flatten () всего в два раза быстрее первой
Christian Geier
Почему что-то вроде float('nan') in xне работает? Я попробовал, и python вернется Falseгде x = [1,2,3,float('nan')].
Чарли Паркер,
1
@CharlieParker по той же причине, почему float ('nan') == float ('nan') вернет False. NaN не равно NaN. Дополнительная информация: stackoverflow.com/questions/10034149/…
Muppet
1
@mab: Это потому, что вызов numpy.anygenexp просто возвращает genexp; вы на самом деле не выполняете вычисления, о которых думаете. Никогда не обращайтесь numpy.anyк генэксп.
user2357112 поддерживает Монику
В реальном сценарии отладки я бы также рекомендовал посмотреть, np.isfiniteа не np.isnanобнаруживать числовые переполнения, нестабильность и т. Д.
Бен Усман,
18

Если бесконечность - возможное значение, я бы использовал 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)
Акавалл
источник
Почему что-то вроде float('nan') in xне работает? Я попробовал, и python вернется Falseгде x = [1,2,3,float('nan')].
Чарли Паркер,
1
@CharlieParker, потому что два nans не считаются равными друг другу. Попробуй float('nan') == float('nan').
Акавалл,
интересный. Почему они не считаются равными?
Чарли Паркер,
1
@CharlieParker, не думаю, что смогу дать здесь хороший ответ. Может быть, это то, что вы ищете: stackoverflow.com/questions/1565164/…
Акавалл,
5

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

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

  • перебирает все данные, независимо от того, найден ли nan
  • создает временный массив размера 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

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

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 порядка (в некоторых случаях). Не так уж и плохо для простой аннотации.

user48956
источник
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 в сравнениях не согласовывалась в более ранних версиях.


источник
Почему что-то вроде float('nan') in xне работает? Я попробовал, и python вернется Falseгде x = [1,2,3,float('nan')].
Чарли Паркер,
@CharlieParker ... потому что сравнение с NAN не дает того, чего вы ожидаете. NAN рассматривается как логический NULL (= не знаю). float("nan")==float("nan")give False(хотя возможно, он должен вернуть NAN или None). Точно так же странность с NAN и boolen NULL верна для многих языков, включая SQL (где NULL = NULL никогда не бывает истиной).
user48956 04
2

(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"
Тинг Он Чан
источник