NumPy에서 NaN에 대한 빠른 확인


120

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

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

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


이 시나리오에서 사용자 입력 유효성 검사가 작동하지 않습니까? 삽입 전 NaN 확인
Woot4Moo

@ Woot4Moo : 아니요, 라이브러리는 NumPy 배열 또는 scipy.sparse행렬을 입력으로 사용합니다.
Fred Foo

2
이 작업을 많이하신다면 Bottleneck ( pypi.python.org/pypi/Bottleneck ) 에 대한 좋은 소식을 들었습니다
matt

답변:


160

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

1
np.min배열에 예상 입력 인 NaN이 포함되어 있지 않을 때 더 빠릅니다. 이 잡는다 때문에하지만, 어쨌든이 일을 수락하기로 결정했습니다 infneginf뿐만 아니라.
Fred Foo

2
이것은 입력에 둘 다 포함 inf되거나 -inf모두 포함되는 경우 에만 포착 되며, 입력에 함께 추가 될 때 오버플로되는 유한 한 값이 포함되어 있으면 문제가 발생합니다.
user2357112 모니카 지원

4
min 및 max는 sse 가능 x86 칩에서 부동 소수점 데이터에 대해 분기 할 필요가 없습니다. 따라서 numpy 1.8 분은 합계보다 느리지 않을 것이며, 내 amd 현상에서는 20 % 더 빠릅니다.
jtaylor

1
내 Intel Core i5에서 OSX의 numpy 1.9.2 np.sum는 여전히 np.min.
Matthew Brett

np.isnan(x).any(0)보다 약간 빠릅니다 np.sumnp.min원하지 않는 캐싱이있을 수 있지만, 내 컴퓨터에서.
jsignell

28

나는 np.isnan(np.min(X))당신이 원하는 것을해야 한다고 생각 합니다.


흠 ... 이것은 O (1) 일 수있을 때 항상 O (n)입니다 (일부 배열의 경우).
user48956

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


1
기본 BLAS 구현 및 C 컴파일러처럼 OS에 크게 의존하지 않는다고 생각합니다. 고맙지 만 내적 x은 큰 값을 포함 할 때 오버플로 될 가능성이 약간 더 높으며 inf도 확인하고 싶습니다.
Fred Foo

1
글쎄, 당신은 항상 1과 내적을 할 수 있고 isfinite(.). 저는 엄청난 성능 차이를 지적하고 싶었습니다. 감사합니다
먹기

내 컴퓨터에서도 마찬가지입니다.
kawing-chiu

1
영리하지 않습니까? Fred Foo가 제안 했듯이 내적 기반 접근 방식의 효율성 향상은 ATLAS, MKL 또는 OpenBLAS와 같은 최적화 된 BLAS 구현에 연결된 로컬 NumPy 설치 덕분에 거의 확실합니다. 예를 들어 Anaconda의 경우입니다. 이 점을 감안할 때이 내적은 사용 가능한 모든 코어에서 병렬화됩니다 . 동일은 없습니다 라고 할 min또는 - sum단일 코어에 국한 실행할 기반 접근 방식. Ergo, 그 성능 격차.
Cecil Curry

16

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

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

4
  1. .any () 사용

    if numpy.isnan(myarray).any()

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

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


3

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


1

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

index = next((i for (i,n) in enumerate(iterable) if n!=n), None)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.