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


답변

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

  • 각 어레이 항목을 확인 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)",
)


답변

  1. .any () 사용

    if numpy.isnan(myarray).any()

  2. numpy.isfinite는 확인을 위해 isnan보다 낫습니다.

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


답변

당신이 편안하다면 빠른 단락 (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기능 과 동일한 작업을 수행합니다 .


답변

이와 관련하여 NaN의 첫 번째 발생을 찾는 방법에 대한 질문이 있습니다. 이것이 내가 아는 것을 처리하는 가장 빠른 방법입니다.

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