检测NumPy数组是否包含至少一个非数字值?

Sal*_*ley 90 python 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
Run Code Online (Sandbox Code Playgroud)

Pau*_*aul 157

这应该比迭代更快,并且无论形状如何都可以工作.

numpy.isnan(myarray).any()
Run Code Online (Sandbox Code Playgroud)

编辑:快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
Run Code Online (Sandbox Code Playgroud)

结果:

  0.11 s numpy.isnan(a).any()
  3.75 s any(numpy.isnan(x) for x in a.flatten())
Run Code Online (Sandbox Code Playgroud)

额外:它适用于非数组NumPy类型:

>>> a = numpy.float64(42.)
>>> numpy.isnan(a).any()
False
>>> a = numpy.float64(numpy.nan)
>>> numpy.isnan(a).any()
True
Run Code Online (Sandbox Code Playgroud)

  • @CharlieParker 与 float('nan') == float('nan') 返回 False 的原因相同。NaN 不等于 NaN。这里有更多信息:http://stackoverflow.com/questions/10034149/why-is-nan-not-equal-to-nan (2认同)
  • 出于兴趣——这真的是最快的方法吗?i) `numpy.isnan(a).any()` 不涉及分配一个大的临时数组(它是一个视图)。ii) 如果第一个元素是 NAN,该解决方案是否涉及迭代整个数组?如果我将第一个元素设置为 NAN,这仍然需要大约 5 微秒,这对于数组查找和测试可以完成的工作来说似乎相当慢 - 应该是纳秒,不是吗? (2认同)

Aka*_*all 15

如果无穷大是一个可能的值,我会使用numpy.isfinite

numpy.isfinite(myarray).all()
Run Code Online (Sandbox Code Playgroud)

如果上面的计算结果为True,则myarray包含no numpy.nan,numpy.inf-numpy.infvalues.

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)
Run Code Online (Sandbox Code Playgroud)

  • @CharlieParker,因为两个“nan”不被认为彼此相等。尝试“float('nan') == float('nan')”。 (2认同)

use*_*956 10

噗!微秒!永远不要在微秒内解决可以在纳秒内解决的问题。

请注意接受的答案:

  • 迭代整个数据,无论是否找到 nan
  • 创建一个大小为 N 的临时数组,这是多余的。

更好的解决方案是在找到 NAN 时立即返回 True:

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)
Run Code Online (Sandbox Code Playgroud)

并适用于 n 维:

array1M_nd = array1M.reshape((len(array1M)/2, 2))
assert any_nans(array1M_nd)==True
%timeit any_nans(array1M_nd)  # 774ns
Run Code Online (Sandbox Code Playgroud)

将此与 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
Run Code Online (Sandbox Code Playgroud)

提前退出方法是 3 个数量级或数量级的加速(在某些情况下)。对于简单的注释来说不算太破旧。