program story

NumPy에서 NaN에 대한 빠른 확인

inputbox 2020. 8. 13. 08:18
반응형

NumPy에서 NaN에 대한 빠른 확인


np.nanNumPy 배열에서 NaN ( ) 발생을 확인하는 가장 빠른 방법을 찾고 X있습니다. np.isnan(X)그것은 X.shape잠재적으로 거대한 shape의 부울 배열을 구축하기 때문에 의문의 여지 가 없습니다.

시도 np.nan in X했지만 작동하지 않는 것 같습니다 np.nan != np.nan. 이 작업을 수행하는 빠르고 메모리 효율적인 방법이 있습니까?

( "얼마나 거대"냐고 묻는 사람들에게 : 말할 수 없습니다. 이것은 라이브러리 코드에 대한 입력 유효성 검사입니다.)


Ray의 솔루션이 좋습니다. 그러나 내 컴퓨터 numpy.sum에서는 다음 대신 사용하는 것이 약 2.5 배 빠릅니다 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

와 달리 min, sum는 분기가 필요하지 않으며, 최신 하드웨어에서는 비용이 많이 드는 경향이 있습니다. 이것이 아마도 sum더 빠른 이유 일 것입니다 .

edit 위의 테스트는 어레이 중간에 단일 NaN을 사용하여 수행되었습니다.

minNaN이 없을 때보 다 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

나는 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(.)기반이 가장 안정적인 것 같습니다.


여기에는 두 가지 일반적인 접근 방식이 있습니다.

  • Check each array item for nan and take any.
  • Apply some cumulative operation that preserves nans (like sum) and check its result.

While the first approach is certainly the cleanest, the heavy optimization of some of the cumulative operations (particularly the ones that are executed in BLAS, like dot) can make those quite fast. Note that dot, like some other BLAS operations, are multithreaded under certain conditions. This explains the difference in speed between different machines.

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)",
)

If you're comfortable with it allows to create a fast short-circuit (stops as soon as a NaN is found) 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

If there is no NaN the function might actually be slower than np.min, I think that's because np.min uses multiprocessing for large arrays:

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

But in case there is a NaN in the array, especially if it's position is at low indices, then it's much faster:

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

Similar results may be achieved with Cython or a C extension, these are a bit more complicated (or easily avaiable as bottleneck.anynan) but ultimatly do the same as my anynan function.


  1. use .any()

    if numpy.isnan(myarray).any()

  2. numpy.isfinite maybe better than isnan for checking

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


Related to this is the question of how to find the first occurrence of NaN. This is the fastest way to handle that that I know of:

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

참고URL : https://stackoverflow.com/questions/6736590/fast-check-for-nan-in-numpy

반응형