快速检查NumPy中的NaN

时间:2011-07-18 17:10:05

标签: python numpy nan

我正在寻找检查NumPy数组np.nan中NaN(X)出现次数的最快方法。 np.isnan(X)是不可能的,因为它构建了一个形状为X.shape的布尔数组,这可能是巨大的。

我尝试了np.nan in X,但这似乎不起作用,因为np.nan != np.nan。是否有一种快速且节省内存的方法来完成这项工作?

(对那些会问“多么巨大”的人:我说不出来。这是图书馆代码的输入验证。)

7 个答案:

答案 0 :(得分:136)

Ray的解决方案很好。但是,在我的计算机上,使用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更快的原因。

编辑上面的测试是在阵列中间使用一个NaN进行的。

值得注意的是,min在存在NaN时比在没有NaN时慢。随着NaN越来越接近数组的开头,它似乎也变慢了。另一方面,无论是否存在NaN以及它们位于何处,sum的吞吐量似乎都是恒定的:

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

答案 1 :(得分:24)

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

答案 2 :(得分:17)

即使存在一个公认的答案,我也想演示以下内容(在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(.)似乎是最稳定的。

答案 3 :(得分:7)

这里有两种通用方法:

  • 检查nan的每个数组项,然后选择any
  • 应用一些保留nan的累积操作(如sum)并检查其结果。

虽然第一种方法肯定是最干净的,但是一些累积操作(特别是在BLAS中执行的那些,如dot)的大量优化可以使这些操作非常快。请注意,dot与其他一些BLAS操作一样,在某些条件下是多线程的。这解释了不同机器之间的速度差异。

enter image description here

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)'
    )

答案 4 :(得分:2)

如果您对感到满意,它可以创建快速短路(一旦找到NaN就会停止)功能:

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

但是如果数组中有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扩展可以获得类似的结果,这些结果有点复杂(或者可以像bottleneck.anynan那样容易实现)但最终可以与我的anynan函数一样。

答案 5 :(得分:2)

View
  1. 使用.any()
  2. enter code here

    1. numpy.isfinite可能比isnan更好地检查
    2. if numpy.isnan(myarray).any()

答案 6 :(得分:1)

与此相关的是如何找到第一次出现的NaN的问题。这是处理我所知道的最快的方法:

index = next((i for (i,n) in enumerate(iterable) if n!=n), None)