快速检查NumPy中的NaN

Fre*_*Foo 107 python numpy nan

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

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

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

NPE*_*NPE 146

Ray的解决方案很好.但是,在我的机器上,它的使用速度提高了约2.5倍numpy.sum,代替numpy.min:

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

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

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

值得注意的是,min在NaN存在下比在不存在时更慢.随着NaN越来越接近数组的开头,它似乎也变慢了.另一方面,sum无论是否存在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
Run Code Online (Sandbox Code Playgroud)

  • min和max不需要为具有sse功能的x86芯片上的浮点数据进行分支.因此,numy 1.8 min不会慢于sum,在我的amd phenom上它甚至快20%. (4认同)
  • 如果输入包含两者,它只捕获`inf`或`-inf`,如果输入包含大但有限的值,当它们加在一起时会出现问题. (2认同)

Ray*_*Ray 25

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


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

因此,真正有效的方式可能在很大程度上取决于操作系统.无论如何,dot(.)基础似乎是最稳定的.

  • 好吧,你总是可以用 1 进行点积并使用“isfinite(.)”。我只是想指出巨大的性能差距。谢谢 (2认同)
  • **聪明,不是吗?** 正如 [Fred Foo](/sf/ask/471561331/#comment7991035_6739580) 所建议的,点积的任何效率增益 -基于的方法几乎肯定要归功于与优化的 BLAS 实现(如 ATLAS、MKL 或 OpenBLAS)相关联的本地 NumPy 安装。例如,Anaconda 就是这种情况。鉴于此,该点积将在“所有”可用核心上并行化。对于基于“min”或“sum”的方法来说,*不能*说同样的情况,这些方法仅限于单个核心运行。因此,性能差距。 (2认同)

Nic*_*mer 10

这里有两种通用方法:

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

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

在此输入图像描述

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


MSe*_*ert 7

感到满意,它可以创建一个快速短路(一旦发现 NaN 就停止)函数:

\n\n
import numba as nb\nimport math\n\n@nb.njit\ndef anynan(array):\n    array = array.ravel()\n    for i in range(array.size):\n        if math.isnan(array[i]):\n            return True\n    return False\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

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

但如果数组中有 NaN,特别是如果它的位置处于低索引,那么它会快得多:

\n\n
array = np.random.random(2000000)\narray[100] = np.nan\n\n%timeit anynan(array)          # 1000000 loops, best of 3: 1.93 \xc2\xb5s per loop\n%timeit np.isnan(array.sum())  # 100 loops, best of 3: 4.57 ms per loop\n%timeit np.isnan(array.min())  # 1000 loops, best of 3: 1.65 ms per loop\n
Run Code Online (Sandbox Code Playgroud)\n\n

使用 Cython 或 C 扩展可以实现类似的结果,这些结果有点复杂(或者很容易获得bottleneck.anynan),但最终与我的功能相同anynan

\n


小智 5

  1. 使用 .any()

    if numpy.isnan(myarray).any()

  2. numpy.isfinite 可能比 isnan 更适合检查

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