快速检查NumPy中的NaN

我正在寻找最快的方法来检查NumPy数组X的NaN( np.nan )的发生。 np.isnan(X)np.isnan(X)的,因为它构build了一个形状为X.shape的布尔型数组,它可能是巨大的。

np.nan in X尝试np.nan in X ,但是这似乎不工作,因为np.nan != np.nan 。 有没有一个快速和有效的方法来做到这一点?

(对那些会问“多么巨大”的人来说:我不能说,这是对库代码的inputvalidation。)

雷的解决scheme是好的。 但是,在我的机器上,使用numpy.sum代替numpy.min速度要快2.5倍:

 In [13]: %timeit np.isnan(np.min(x)) 1000 loops, best of 3: 244 us per loop In [14]: %timeit np.isnan(np.sum(x)) 10000 loops, best of 3: 97.3 us per loop 

min不同, sum不需要分支,在现代硬件上往往是相当昂贵的。 这可能是sum更快的原因。

编辑上面的testing是在arrays中间用一个单独的NaN执行的。

值得注意的是,在NaN存在的情况下min比在不存在的情况下慢。 随着NaN接近数组的开始,它似乎也变得越来越慢。 另一方面, sum的吞吐量似乎是不变的,不pipe是否有NaN和它们的位置:

 In [40]: x = np.random.rand(100000) In [41]: %timeit np.isnan(np.min(x)) 10000 loops, best of 3: 153 us per loop In [42]: %timeit np.isnan(np.sum(x)) 10000 loops, best of 3: 95.9 us per loop In [43]: x[50000] = np.nan In [44]: %timeit np.isnan(np.min(x)) 1000 loops, best of 3: 239 us per loop In [45]: %timeit np.isnan(np.sum(x)) 10000 loops, best of 3: 95.8 us per loop In [46]: x[0] = np.nan In [47]: %timeit np.isnan(np.min(x)) 1000 loops, best of 3: 326 us per loop In [48]: %timeit np.isnan(np.sum(x)) 10000 loops, best of 3: 95.9 us per loop 

我认为np.isnan(np.min(X))应该做你想要的。

即使有一个被接受的答案,我想演示下面的内容(在Vista上使用Python 2.7.2和Numpy 1.6.0):

 In []: x= rand(1e5) In []: %timeit isnan(x.min()) 10000 loops, best of 3: 200 us per loop In []: %timeit isnan(x.sum()) 10000 loops, best of 3: 169 us per loop In []: %timeit isnan(dot(x, x)) 10000 loops, best of 3: 134 us per loop In []: x[5e4]= NaN In []: %timeit isnan(x.min()) 100 loops, best of 3: 4.47 ms per loop In []: %timeit isnan(x.sum()) 100 loops, best of 3: 6.44 ms per loop In []: %timeit isnan(dot(x, x)) 10000 loops, best of 3: 138 us per loop 

因此,真正有效的方式可能严重依赖于操作系统。 无论如何dot(.)似乎是最稳定的。

如果你对numba感到满意,它可以创build一个快速短路(只要findNaN就停下来)function:

 import numba as nb import math @nb.njit def anynan(array): array = array.ravel() for i in range(array.size): if math.isnan(array[i]): return True return False 

如果没有NaN函数可能实际上比np.min慢,我认为这是因为np.min使用多处理大型数组:

 import numpy as np array = np.random.random(2000000) %timeit anynan(array) # 100 loops, best of 3: 2.21 ms per loop %timeit np.isnan(array.sum()) # 100 loops, best of 3: 4.45 ms per loop %timeit np.isnan(array.min()) # 1000 loops, best of 3: 1.64 ms per loop 

但是如果在arrays中存在NaN,特别是如果位置处于低指数,则速度要快得多:

 array = np.random.random(2000000) array[100] = np.nan %timeit anynan(array) # 1000000 loops, best of 3: 1.93 µs per loop %timeit np.isnan(array.sum()) # 100 loops, best of 3: 4.57 ms per loop %timeit np.isnan(array.min()) # 1000 loops, best of 3: 1.65 ms per loop 

使用Cython或C扩展可能会得到类似的结果,这些可能会更复杂一些(或者简单地说就像bothne.anynan一样),但是最终会和我的anynan函数一样。

这里有两种一般的方法:

  • 检查每个数组项目nan并采取any
  • 应用一些保留nan (如sum )的累积操作并检查其结果。

虽然第一种方法当然是最干净的,但是对某些累积操作(尤其是那些在BLAS中执行的操作(如dot ))的大量优化可以使这些操作相当快。 请注意,像某些其他BLAS操作一样, dot在某些情况下是multithreading的。 这解释了不同机器之间的速度差异。

在这里输入图像说明

 import numpy import perfplot def min(a): return numpy.isnan(numpy.min(a)) def sum(a): return numpy.isnan(numpy.sum(a)) def dot(a): return numpy.isnan(numpy.dot(a, a)) def any(a): return numpy.any(numpy.isnan(a)) def einsum(a): return numpy.isnan(numpy.einsum('i->', a)) perfplot.show( setup=lambda n: numpy.random.rand(n), kernels=[min, sum, dot, any, einsum], n_range=[2**k for k in range(20)], logx=True, logy=True, xlabel='len(a)' ) 

与此相关的是如何find第一个NaN的问题。 这是处理我所知道的最快的方法:

 index = next((i for (i,n) in enumerate(iterable) if n!=n), None) 
 enter code here 
  1. 使用.any()

if numpy.isnan(myarray).any()

  1. numpy.isfinite可能比isnan更好的检查

if not np.isfinite(prop).all()