Виявити, чи масив 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виглядає підозрілою: "Повертає помилку, якщо існує хоча б одне нечислове значення". Я б очікував contains_nanповернення, Trueякщо масив містить NaN.
Самуель Тардьє

Що щодо вхідних даних, таких як array(['None', 'None'], dtype=object)? Чи повинен такий внесок викликати виключення?
Фінн Еруп Нільсен

НЕ використовуйте float('nan') in x. Це не працює.
Чарлі Паркер

Відповіді:


183

Це має бути швидше, ніж ітерація, і працюватиме незалежно від форми.

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 () удвічі швидша за першу
Крістіан Г'єр

Чому щось на кшталт float('nan') in xне працює? Я спробував це і пітон повертається Falseкуди x = [1,2,3,float('nan')].
Чарлі Паркер

1
@CharlieParker з тієї ж причини, чому float ('nan') == float ('nan') повернеться False. NaN не дорівнює NaN. Ось додаткова інформація: stackoverflow.com/questions/10034149/…
Muppet

1
@mab: Це тому, що виклик numpy.anyу genexp просто повертає genexp; ти насправді не робиш обчислення, які ти думаєш. Ніколи не закликайте numpy.anyдо genexp.
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не працює? Я спробував це і пітон повертається Falseкуди x = [1,2,3,float('nan')].
Чарлі Паркер

1
@CharlieParker, оскільки два nans не вважаються рівними один одному. Спробуйте float('nan') == float('nan').
Акавал

цікаво. Чому їх не вважають рівними?
Чарлі Паркер

1
@CharlieParker, я не думаю, що я міг би дати тут дуже гарну відповідь. Може бути , це те , що ви шукаєте: stackoverflow.com/questions/1565164 / ...
Akavall

4

Pfft! Мікросекунди! Ніколи не вирішуйте проблему в мікросекундах, яку можна вирішити за наносекунди.

Зауважте, що прийнята відповідь:

  • повторюється над усіма даними, незалежно від того, чи знайдена няня
  • створює тимчасовий масив розміром 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 порядки або прискорення величини (в деяких випадках). Не надто пошарпаний для простої анотації.


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

Лікування нан у порівняннях не було послідовним у попередніх версіях.


Чому щось на кшталт float('nan') in xне працює? Я спробував це і пітон повертається Falseкуди x = [1,2,3,float('nan')].
Чарлі Паркер

@CharlieParker ... тому що порівняння з NAN не робить те, що ви очікуєте. NAN трактується як логічний NULL (= не знаю). float("nan")==float("nan")дайте False(хоча можливо, це, мабуть, має повернути NAN або None). Аналогічне дивацтво з NAN і булевим NULL справедливо для багатьох мов, включаючи SQL (де NULL = NULL ніколи не відповідає дійсності).
user48956

2

(np.where(np.isnan(A)))[0].shape[0]буде більше, ніж 0якщо Aмістить принаймні один елемент nan, Aможе бути ann 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"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.