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