source

기존 값보다 큰 값의 첫 번째 발생 횟수

factcode 2022. 9. 12. 11:36
반응형

기존 값보다 큰 값의 첫 번째 발생 횟수

1D 배열을 numpy로 하고 있는데 값이 numpy 배열의 값을 초과하는 인덱스의 위치를 찾고 싶습니다.

예.

aa = range(-10,10)

aa서, 값 「」, 「」5과됩니니다다

이게 좀 빨라요(더 좋아 보여요)

np.argmax(aa>5)

처음부터 멈출 것이기 때문이다.True("최대값이 여러 개 발생할 경우 첫 번째 항목에 해당하는 인덱스가 반환됩니다.") 다른 목록은 저장되지 않습니다.

In [2]: N = 10000

In [3]: aa = np.arange(-N,N)

In [4]: timeit np.argmax(aa>N/2)
100000 loops, best of 3: 52.3 us per loop

In [5]: timeit np.where(aa>N/2)[0][0]
10000 loops, best of 3: 141 us per loop

In [6]: timeit np.nonzero(aa>N/2)[0][0]
10000 loops, best of 3: 142 us per loop

어레이의 소트된 콘텐츠를 고려하면 검색 정렬이라는 보다 빠른 방법이 있습니다.

import time
N = 10000
aa = np.arange(-N,N)
%timeit np.searchsorted(aa, N/2)+1
%timeit np.argmax(aa>N/2)
%timeit np.where(aa>N/2)[0][0]
%timeit np.nonzero(aa>N/2)[0][0]

# Output
100000 loops, best of 3: 5.97 µs per loop
10000 loops, best of 3: 46.3 µs per loop
10000 loops, best of 3: 154 µs per loop
10000 loops, best of 3: 154 µs per loop

저도 이 문제에 관심이 있어서 제안된 모든 답변을 성능도와 비교했습니다. (해임자:저는 perfplot의 저자입니다.)

참조하고 있는 어레이가 이미 정렬되어 있는 경우는,

numpy.searchsorted(a, alpha)

널 위한 거야O(log(n) 연산입니다.즉, 속도는 어레이의 크기에 거의 의존하지 않습니다.그보다 더 빨리 갈 순 없어

어레이에 대해 아무것도 모르더라도 이 어레이에 대해 잘못 알고 있는 것은 아닙니다.

numpy.argmax(a > alpha)

이미 정렬됨:

여기에 이미지 설명 입력

정렬되지 않음:

여기에 이미지 설명 입력

플롯을 재현하는 코드:

import numpy
import perfplot


alpha = 0.5
numpy.random.seed(0)


def argmax(data):
    return numpy.argmax(data > alpha)


def where(data):
    return numpy.where(data > alpha)[0][0]


def nonzero(data):
    return numpy.nonzero(data > alpha)[0][0]


def searchsorted(data):
    return numpy.searchsorted(data, alpha)


perfplot.save(
    "out.png",
    # setup=numpy.random.rand,
    setup=lambda n: numpy.sort(numpy.random.rand(n)),
    kernels=[argmax, where, nonzero, searchsorted],
    n_range=[2 ** k for k in range(2, 23)],
    xlabel="len(array)",
)
In [34]: a=np.arange(-10,10)

In [35]: a
Out[35]:
array([-10,  -9,  -8,  -7,  -6,  -5,  -4,  -3,  -2,  -1,   0,   1,   2,
         3,   4,   5,   6,   7,   8,   9])

In [36]: np.where(a>5)
Out[36]: (array([16, 17, 18, 19]),)

In [37]: np.where(a>5)[0][0]
Out[37]: 16

요소 간에 일정한 단계가 있는 배열

range또는 단순히 프로그래밍 방식으로 인덱스를 계산할 수 있습니다. 실제로 어레이에서 반복할 필요가 없습니다.

def first_index_calculate_range_like(val, arr):
    if len(arr) == 0:
        raise ValueError('no value greater than {}'.format(val))
    elif len(arr) == 1:
        if arr[0] > val:
            return 0
        else:
            raise ValueError('no value greater than {}'.format(val))

    first_value = arr[0]
    step = arr[1] - first_value
    # For linearly decreasing arrays or constant arrays we only need to check
    # the first element, because if that does not satisfy the condition
    # no other element will.
    if step <= 0:
        if first_value > val:
            return 0
        else:
            raise ValueError('no value greater than {}'.format(val))

    calculated_position = (val - first_value) / step

    if calculated_position < 0:
        return 0
    elif calculated_position > len(arr) - 1:
        raise ValueError('no value greater than {}'.format(val))

    return int(calculated_position) + 1

그것은 조금 개선될 수 있을 것이다.몇 가지 샘플 어레이와 값에서 올바르게 동작하고 있는지 확인했지만, 특히 플로트를 사용하고 있는 것을 고려하면, 거기에 실수가 없는 것은 아닙니다.

>>> import numpy as np
>>> first_index_calculate_range_like(5, np.arange(-10, 10))
16
>>> np.arange(-10, 10)[16]  # double check
6

>>> first_index_calculate_range_like(4.8, np.arange(-10, 10))
15

하지 않고 할 수 그것은 일정한 .O(1)그리고, 그 외의 모든 어프로치를 능가할 가능성이 있습니다.그러나 어레이에서 일정한 단계를 수행해야 합니다. 그렇지 않으면 잘못된 결과가 발생합니다.

numba를 사용한 일반적인 솔루션

보다 일반적인 접근방식은 numba 함수를 사용하는 것입니다.

@nb.njit
def first_index_numba(val, arr):
    for idx in range(len(arr)):
        if arr[idx] > val:
            return idx
    return -1

이는 모든 어레이에서 작동하지만 어레이에서 반복해야 하기 때문에 평균적인 경우 다음과 같습니다.O(n):

>>> first_index_numba(4.8, np.arange(-10, 10))
15
>>> first_index_numba(5, np.arange(-10, 10))
16

벤치마크

Nico Schlömer는 이미 몇 가지 벤치마크를 제공했지만, 새로운 솔루션을 포함하여 다양한 "가치"를 테스트하는 것이 유용할 것으로 생각했습니다.

테스트 셋업:

import numpy as np
import math
import numba as nb

def first_index_using_argmax(val, arr):
    return np.argmax(arr > val)

def first_index_using_where(val, arr):
    return np.where(arr > val)[0][0]

def first_index_using_nonzero(val, arr):
    return np.nonzero(arr > val)[0][0]

def first_index_using_searchsorted(val, arr):
    return np.searchsorted(arr, val) + 1

def first_index_using_min(val, arr):
    return np.min(np.where(arr > val))

def first_index_calculate_range_like(val, arr):
    if len(arr) == 0:
        raise ValueError('empty array')
    elif len(arr) == 1:
        if arr[0] > val:
            return 0
        else:
            raise ValueError('no value greater than {}'.format(val))

    first_value = arr[0]
    step = arr[1] - first_value
    if step <= 0:
        if first_value > val:
            return 0
        else:
            raise ValueError('no value greater than {}'.format(val))

    calculated_position = (val - first_value) / step

    if calculated_position < 0:
        return 0
    elif calculated_position > len(arr) - 1:
        raise ValueError('no value greater than {}'.format(val))

    return int(calculated_position) + 1

@nb.njit
def first_index_numba(val, arr):
    for idx in range(len(arr)):
        if arr[idx] > val:
            return idx
    return -1

funcs = [
    first_index_using_argmax, 
    first_index_using_min, 
    first_index_using_nonzero,
    first_index_calculate_range_like, 
    first_index_numba, 
    first_index_using_searchsorted, 
    first_index_using_where
]

from simple_benchmark import benchmark, MultiArgument

플롯은 다음을 사용하여 생성되었습니다.

%matplotlib notebook
b.plot()

항목이 선두에 있습니다.

b = benchmark(
    funcs,
    {2**i: MultiArgument([0, np.arange(2**i)]) for i in range(2, 20)},
    argument_name="array size")

여기에 이미지 설명 입력

numba 함수가 가장 잘 수행되고 계산 함수 및 검색 정렬 함수가 이어집니다.다른 솔루션은 성능이 훨씬 떨어집니다.

항목이 끝에 있습니다.

b = benchmark(
    funcs,
    {2**i: MultiArgument([2**i-2, np.arange(2**i)]) for i in range(2, 20)},
    argument_name="array size")

여기에 이미지 설명 입력

소형 어레이의 경우 numba 함수는 놀라울 정도로 고속이지만, 대형 어레이의 경우 계산 기능이나 검색 정렬 기능보다 성능이 우수합니다.

항목이 sqrt(len)에 있습니다.

b = benchmark(
    funcs,
    {2**i: MultiArgument([np.sqrt(2**i), np.arange(2**i)]) for i in range(2, 20)},
    argument_name="array size")

여기에 이미지 설명 입력

이게 더 재밌어요.다시 numba와 계산 함수는 훌륭하지만, 이것은 실제로는 최악의 검색 정렬을 트리거하고 있으며, 이 경우 제대로 작동하지 않습니다.

조건을 만족하는 값이 없는 경우의 함수 비교

또 다른 흥미로운 점은 인덱스를 반환해야 하는 값이 없을 경우 이러한 함수가 어떻게 동작하는지에 대한 것입니다.

arr = np.ones(100)
value = 2

for func in funcs:
    print(func.__name__)
    try:
        print('-->', func(value, arr))
    except Exception as e:
        print('-->', e)

이 결과:

first_index_using_argmax
--> 0
first_index_using_min
--> zero-size array to reduction operation minimum which has no identity
first_index_using_nonzero
--> index 0 is out of bounds for axis 0 with size 0
first_index_calculate_range_like
--> no value greater than 2
first_index_numba
--> -1
first_index_using_searchsorted
--> 101
first_index_using_where
--> index 0 is out of bounds for axis 0 with size 0

searchsorted, argmax 및 numba는 단순히 잘못된 값을 반환합니다.하지만searchsorted그리고.numba배열에 유효한 인덱스가 아닌 인덱스를 반환합니다.

기능where,min,nonzero그리고.calculate예외를 두다단, 에 대한 예외입니다.calculate도움이 되는 말은 뭐든 할 수 있어요

즉, 이러한 콜을 적절한 래퍼 함수로 랩핑하여 예외 또는 비활성 반환값을 검출하고 적어도 값이 어레이 내에 있는지 여부를 확신할 수 없는 경우 적절하게 처리해야 합니다.


주의: 계산과searchsorted옵션은 특수한 조건에서만 작동합니다."계산" 함수를 사용하려면 일정한 단계가 필요하며 검색 정렬을 위해서는 배열이 정렬되어야 합니다.따라서 이러한 방법은 적절한 상황에서 유용할 수 있지만 이 문제에 대한 일반적인 해결책은 아닙니다.정렬된 Python 목록을 다루는 경우 Numpys 검색 정렬을 사용하는 대신 이등분 모듈을 살펴보는 것이 좋습니다.

제안하고 싶습니다.

np.min(np.append(np.where(aa>5)[0],np.inf))

그러면 조건이 충족되는 가장 작은 인덱스가 반환되고 조건이 충족되지 않으면 무한대가 반환됩니다.where빈 배열을 반환합니다).

같이 가고 싶다

i = np.min(np.where(V >= x))

어디에Vis 벡터(1d 배열),x가치 및i는 결과 인덱스입니다.

대신사용해야 합니다.후자는 값을 찾을 수 없는 경우에도 위치 0을 반환합니다.이것은 예상한 인덱스가 아닙니다.

>>> aa = np.array(range(-10,10))
>>> print(aa)
array([-10,  -9,  -8,  -7,  -6,  -5,  -4,  -3,  -2,  -1,   0,   1,   2,
         3,   4,   5,   6,   7,   8,   9])

조건이 충족되면 인덱스 배열을 반환합니다.

>>> idx = np.where(aa > 5)[0]
>>> print(idx)
array([16, 17, 18, 19], dtype=int64)

그렇지 않으면 빈 배열을 반환합니다.

>>> not_found = len(np.where(aa > 20)[0])
>>> print(not_found)
array([], dtype=int64)

에에에 argmax이 경우: 솔루션이 모호하지 않다면 단순할수록 좋습니다.그래서 이 상태에 빠졌는지를 확인하기 위해서if len(np.where(aa > value_to_search)[0]) > 0.

언급URL : https://stackoverflow.com/questions/16243955/numpy-first-occurrence-of-value-greater-than-existing-value

반응형