programing

NumPy에서 NaN에 대한 빠른 확인

lovejava 2023. 7. 18. 21:24

NumPy에서 NaN에 대한 빠른 확인

NaN 발생 여부를 가장 빠르게 확인할 수 있는 방법을 찾고 있습니다(np.nan에서 ) NumPy 배열표 니다됩시로▁)다.X.np.isnan(X)합니다.X.shape잠재적으로 거대할 수도 있습니다.

나는 노력했다.np.nan in X하지만 그것은 작동하지 않는 것처럼 보입니다 왜냐하면np.nan != np.nan빠르고 메모리 효율적인 방법이 있습니까?

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

레이의 해결책은 좋습니다.하지만, 내 기계에서는 대신 사용하는 것이 약 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이 배열의 시작에 가까워질수록 속도가 느려지는 것으로 보입니다. 에반면은,sum한 것으로 .:NaNaN의 경우 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))당신이 원하는 것을 해야 합니다.

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

  • 에서 다에대각어항확인목이레음한▁check▁each 확인합니다.nan그리고 테이크any.
  • 보하는일누작적용업을 보존하는 합니다.nan요)sum 및합니다. 및 결과를 확인합니다.

첫 번째 접근 방식이 확실히 가장 깨끗하지만, 일부 누적 작업(특히 BLAS에서 실행되는 작업)의 강력한 최적화는 다음과 같습니다.dot를 사용하면 수 는 그것들을 꽤 빠르게 만들 수 있습니다.:dot다른 일부 BLAS 작업과 마찬가지로, 특정 조건에서 멀티 스레드화됩니다.이것은 서로 다른 기계 간의 속도 차이를 설명합니다.

enter image description here

import numpy as np
import perfplot


def min(a):
    return np.isnan(np.min(a))


def sum(a):
    return np.isnan(np.sum(a))


def dot(a):
    return np.isnan(np.dot(a, a))


def any(a):
    return np.any(np.isnan(a))


def einsum(a):
    return np.isnan(np.einsum("i->", a))


b = perfplot.bench(
    setup=np.random.rand,
    kernels=[min, sum, dot, any, einsum],
    n_range=[2 ** k for k in range(25)],
    xlabel="len(a)",
)
b.save("out.png")
b.show()

승인된 답변이 있더라도 다음을 시연해 보겠습니다(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이 발견되는 즉시 중지) 기능을 만들 수 있습니다.

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 확장으로 유사한 결과를 얻을 수 있습니다. 이것들은 조금 더 복잡합니다(또는 쉽게 사용할 수 있습니다). 하지만 궁극적으로 제 것과 동일하게 합니다.anynan기능.

  1. .any를 사용합니다.

    if numpy.isnan(myarray).any()

  2. numpy.isfinite는 확인을 위해 nan보다 더 나을 수 있습니다.

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

이와 관련하여 NaN의 첫 발생을 어떻게 찾을 것인가에 대한 문제가 있습니다.이것이 제가 알고 있는 가장 빠른 처리 방법입니다.

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

@nico-schlömer와 @mseifert의 답변을 추가하여 numba-test의 성능을 계산했습니다.has_nan전체 배열을 구문 분석하는 일부 함수와 비교하여 초기 중지를 사용합니다.

내 시스템에서 nan이 없는 어레이의 경우 ~10^4 요소에 대해 손익분기점이 발생합니다.

has_nan_vs_full_parse_methods


import perfplot
import numpy as np
import numba
import math

def min(a):
    return np.isnan(np.min(a))

def dot(a):
    return np.isnan(np.dot(a, a))

def einsum(a):
    return np.isnan(np.einsum("i->", a))

@numba.njit
def has_nan(a):
    for i in range(a.size - 1):
        if math.isnan(a[i]):
            return True
    return False


def array_with_missing_values(n, p):
    """ Return array of size n,  p : nans ( % of array length )
    Ex : n=1e6, p=1 : 1e4 nan assigned at random positions """
    a = np.random.rand(n)
    p = np.random.randint(0, len(a), int(p*len(a)/100))
    a[p] = np.nan
    return a


#%%
perfplot.show(
    setup=lambda n: array_with_missing_values(n, 0),
    kernels=[min, dot, has_nan],
    n_range=[2 ** k for k in range(20)],
    logx=True,
    logy=True,
    xlabel="len(a)",
)

배열에 nans가 있으면 어떻게 됩니까?저는 어레이의 나노 커버리지의 영향을 조사했습니다.

길이가 1,000,000인 배열의 경우,has_nan배열에 ~10^-3%의 nans(따라서 ~10nans)가 있는 경우 더 나은 옵션이 됩니다.

impact of nan-coverage of array


#%%
N = 1000000  # 100000
perfplot.show(
    setup=lambda p: array_with_missing_values(N, p),
    kernels=[min, dot, has_nan],
    n_range=np.array([2 ** k for k in range(20)]) / 2**20 * 0.01, 
    logy=True,
    xlabel=f"% of nan in array (N = {N})",
)

의 어레이에 애리케에대어의가가 있는 nan 그 다음에 고당없는것을찾있고습다니그신럼은그, 리럼▁and▁without.has_nan최선의 방법입니다. 기타;dot최선의 선택인 것 같습니다.

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