Определить, содержит ли массив NumPy хотя бы одно нечисловое значение?
Вопрос
Мне нужно написать функцию, которая определит, содержит ли ввод хотя бы одно нечисловое значение.Если будет найдено нечисловое значение, я выдам сообщение об ошибке (поскольку вычисление должно возвращать только числовое значение).Количество измерений входного массива заранее неизвестно - функция должна выдавать правильное значение независимо от ndim.В качестве дополнительного усложнения входными данными могут быть значения с одним значением float или numpy.float64
или даже что-то странное вроде нулевого размерного массива.
Очевидный способ решить эту проблему - написать рекурсивную функцию, которая перебирает каждый итерабельный объект в массиве до тех пор, пока не найдет неитерабельный.Это будет применять numpy.isnan()
функционируйте над каждым не итерируемым объектом.Если найдено хотя бы одно нечисловое значение, то функция немедленно вернет False.В противном случае, если все значения в iterable являются числовыми, это в конечном итоге вернет 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
Решение
Это должно быть быстрее, чем повторение, и будет работать независимо от формы.
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
Другие советы
Если бесконечность - это возможное значение, я бы использовал numpy.конечный
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)
С помощью 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 при сравнениях не была последовательной в более ранних версиях.
(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"
Пффф!Микросекунды!Никогда не решайте проблему за микросекунды, которая может быть решена за наносекунды.
Обратите внимание, что принятый ответ:
- выполняет итерацию по всем данным, независимо от того, найден ли 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 порядка или величину (в некоторых случаях).Не слишком убого для простой аннотации.