IT

Numpy : 빠른 가치의 첫 번째 눈길 찾기

lottoking 2020. 8. 28. 19:35
반응형

Numpy : 빠른 가치의 첫 번째 눈길 찾기


Numpy 배열에서 숫자가 처음으로 사용할 수있는 어떻게 사용할 수 있습니까? 나에게는 속도가 중요합니다. 나는 전체 배열을 스캔하고 첫 번째 발생하지 않습니다.

itemindex = numpy.where(array==item)[0][0]
nonzero(array == item)[0][0]

참고 1 : 해당 질문의 답변 중 어느 것도 관련 이없는있습니다. 배열에서 서술의 첫 번째 보안을 반환하는 Numpy 함수가?

참고 2 : 사용하는 것이 Python 루프보다 선호됩니다.


Numpy 2.0.0에 대해 예정된 기능 요청이 있습니다 : https://github.com/numpy/numpy/issues/2269


너무 늦었지만 나중에 참조 할 수 있습니다. numba ( 1 )를 사용 하는 것이 numpy가 구현할 때까지 가장 쉬운 방법입니다. 아나콘다 설치되어 배포판을 사용한다면 이미 설치되어있을 것입니다. 코드가 단어 생략

@jit(nopython=True)
def find_first(item, vec):
    """return the index of the first occurence of item in vec"""
    for i in xrange(len(vec)):
        if item == vec[i]:
            return i
    return -1

그리고 :

>>> a = array([1,7,8,32])
>>> find_first(8,a)
2

몇 가지 방법에 대한 벤치 마크를 만들었습니다.

  • argwhere
  • nonzero 질문에서와 같이
  • .tostring() @Rob Reilink의 답변에서와 같이
  • 다른 루프
  • Fortran 루프

추가포트란 코드를 사용할 수 있습니다. 나는 목록으로 변환하는 것과 같이 유망하지 않은 것을 건너 뛰었습니다.

로그의 결과. X 축은 바늘의 위치입니다 (배열 아래에 있는지 찾는 데 시간이 더 오래 있습니다). 마지막 값은 배열에없는 바늘입니다. Y 축은 그것을 사용 시간입니다.

벤치 마크 결과

어레이에는 1 백만 개의 요소가 있고 100 번 실행되었습니다. 결과는 여전히 약간의 변동이 있다고 정성적인 추세는 분명합니다. Python과 f2py는 첫 번째 요소에서 종료되어 다른 방식으로 확장됩니다. 사용하는 것이 처음 1 %에 있지 않습니다 f2py.

요약하면, f2py는 특히 바늘이 조기에 가장 빠른 솔루션 입니다.

성가신 내장은 실제로 2 분만에 작업 할 수 있습니다. 추가 라는 파일 search.f90:

subroutine find_first(needle, haystack, haystack_length, index)
    implicit none
    integer, intent(in) :: needle
    integer, intent(in) :: haystack_length
    integer, intent(in), dimension(haystack_length) :: haystack
!f2py intent(inplace) haystack
    integer, intent(out) :: index
    integer :: k
    index = -1
    do k = 1, haystack_length
        if (haystack(k)==needle) then
            index = k - 1
            exit
        endif
    enddo
end

이외의 것을 소유하고있는 것을 integer변경하십시오. 그런 다음 다음을 사용하여 준비하십시오.

f2py -c -m search search.f90

그 후에 할 수 있습니다 (Python에서) :

import search
print(search.find_first.__doc__)
a = search.find_first(your_int_needle, your_int_array)

array.tostring()다음과 같이 find () 메서드를 사용 하여 부울 배열을 Python 문자열로 변환 할 수 있습니다 .

(array==item).tostring().find('\x01')

하지만 파이썬 문자열은 변경 불가능해야하므로 데이터 복사가 포함됩니다. 장점은 예를 들어 상승 에지를 찾아서 검색 할 수도 있다는 것입니다.\x00\x01


정렬 된 배열의 경우 np.searchsorted작동합니다.


다른 방법과 배열에 대한 사전 지식이 실제로 도움 이되는 문제에 부딪혔다 고 생각합니다 . 데이터의 첫 Y %에서 답을 찾을 확률이 X 인 경우입니다. 운이 좋을 것이라는 희망으로 문제를 나누고 중첩 된 목록 이해 또는 무언가로 파이썬에서 이것을 수행합니다.

이 무차별 대입을 수행하기 위해 C 함수를 작성하는 것도 ctypes사용하여 그리 어렵지 않습니다 .

내가 함께 해킹 한 C 코드 (index.c) :

long index(long val, long *data, long length){
    long ans, i;
    for(i=0;i<length;i++){
        if (data[i] == val)
            return(i);
    }
    return(-999);
}

그리고 파이썬 :

# to compile (mac)
# gcc -shared index.c -o index.dylib
import ctypes
lib = ctypes.CDLL('index.dylib')
lib.index.restype = ctypes.c_long
lib.index.argtypes = (ctypes.c_long, ctypes.POINTER(ctypes.c_long), ctypes.c_long)

import numpy as np
np.random.seed(8675309)
a = np.random.random_integers(0, 100, 10000)
print lib.index(57, a.ctypes.data_as(ctypes.POINTER(ctypes.c_long)), len(a))

92를 얻습니다.

파이썬을 적절한 기능으로 감싸면됩니다.

C 버전은이 시드에 대해 훨씬 빠릅니다 (~ 20 배).

import timeit
t = timeit.Timer('np.where(a==57)[0][0]', 'import numpy as np; np.random.seed(1); a = np.random.random_integers(0, 1000000, 10000000)')
t.timeit(100)/100
# 0.09761879920959472
t2 = timeit.Timer('lib.index(57, a.ctypes.data_as(ctypes.POINTER(ctypes.c_long)), len(a))', 'import numpy as np; np.random.seed(1); a = np.random.random_integers(0, 1000000, 10000000); import ctypes; lib = ctypes.CDLL("index.dylib"); lib.index.restype = ctypes.c_long; lib.index.argtypes = (ctypes.c_long, ctypes.POINTER(ctypes.c_long), ctypes.c_long) ')
t2.timeit(100)/100
# 0.005288000106811523

@tal은 이미 numba첫 번째 인덱스를 찾는 함수를 제공 했지만 1D 배열에서만 작동합니다. 를 사용 np.ndenumerate하면 임의 차원 배열에서 첫 번째 인덱스를 찾을 수도 있습니다.

from numba import njit
import numpy as np

@njit
def index(array, item):
    for idx, val in np.ndenumerate(array):
        if val == item:
            return idx
    return None

샘플 케이스 :

>>> arr = np.arange(9).reshape(3,3)
>>> index(arr, 3)
(1, 0)

타이밍은 성능이 tals 솔루션 과 유사 함을 보여줍니다 .

arr = np.arange(100000)
%timeit index(arr, 5)           # 1000000 loops, best of 3: 1.88 µs per loop
%timeit find_first(5, arr)      # 1000000 loops, best of 3: 1.7 µs per loop

%timeit index(arr, 99999)       # 10000 loops, best of 3: 118 µs per loop
%timeit find_first(99999, arr)  # 10000 loops, best of 3: 96 µs per loop

내가 아는 한 부울 배열의 np.any 및 np.all 만 단락됩니다.

귀하의 경우 numpy는 부울 조건을 생성하기 위해 한 번, 인덱스를 찾기 위해 두 번 전체 배열을 두 번 통과해야합니다.

이 경우 나의 권장 사항은 cython을 사용하는 것입니다. 특히 다른 dtype과 모양에 대해 많은 유연성이 필요하지 않은 경우이 경우에 대한 예제를 조정하는 것이 쉬워야한다고 생각합니다.


나는 이것이 내 직업에 필요했기 때문에 Python과 Numpy의 C 인터페이스를 스스로 가르치고 직접 작성했습니다. http://pastebin.com/GtcXuLyd 1D 배열에만 해당되지만 대부분의 데이터 유형 (int, float 또는 string)에서 작동하며 테스트 결과 순수한 Python에서 예상되는 접근 방식보다 약 20 배 더 빠릅니다. numpy.


목록이 정렬 된 경우 'bisect'패키지를 사용하여 색인을 매우 빠르게 검색 할 수 있습니다 . O (n) 대신 O (log (n))입니다.

bisect.bisect(a, x)

배열 a에서 x를 찾습니다. 정렬 된 경우 모든 첫 번째 요소를 통과하는 C 루틴보다 확실히 더 빠릅니다 (충분한 목록의 경우).

때때로 아는 것이 좋습니다.


오랜 matlab 사용자로서 나는 꽤 오랫동안이 문제에 대한 효율적인 솔루션을 찾고 있습니다. 마지막으로,이 스레드 의 제안에 대한 토론을 통해 여기 에 제안 된 것과 유사한 API를 구현하는 솔루션을 생각해 보았습니다 . 현재로서는 1D 배열 만 지원합니다.

당신은 이것을 이렇게 사용할 것입니다

import numpy as np
import utils_find_1st as utf1st
array = np.arange(100000)
item = 1000
ind = utf1st.find_1st(array, item, utf1st.cmp_larger_eq)

지원되는 조건 연산자는 cmp_equal, cmp_not_equal, cmp_larger, cmp_smaller, cmp_larger_eq, cmp_smaller_eq입니다. 효율성을 위해 확장은 c로 작성됩니다.

여기에서 소스, 벤치 마크 및 기타 세부 정보를 찾을 수 있습니다.

https://pypi.python.org/pypi?name=py_find_1st&:action=display

우리 팀에서 사용하기 위해 (리눅스 및 macOS의 아나콘다) 설치를 단순화하는 아나콘다 설치 프로그램을 만들었습니다. 여기에 설명 된대로 사용할 수 있습니다.

https://anaconda.org/roebel/py_find_1st


일련의 검색을 수행하는 경우 검색 차원이 충분히 크지 않으면 문자열로 변환하는 것과 같은 영리한 작업을 수행하여 얻을 수있는 성능 향상이 외부 루프에서 손실 될 수 있습니다. 위에서 제안한 문자열 변환 트릭을 ​​사용하는 find1과 내부 축을 따라 argmax를 사용하는 find2를 반복하는 성능을 확인하십시오 (불일치가 -1로 반환되도록 조정).

import numpy,time
def find1(arr,value):
    return (arr==value).tostring().find('\x01')

def find2(arr,value): #find value over inner most axis, and return array of indices to the match
    b = arr==value
    return b.argmax(axis=-1) - ~(b.any())


for size in [(1,100000000),(10000,10000),(1000000,100),(10000000,10)]:
    print(size)
    values = numpy.random.choice([0,0,0,0,0,0,0,1],size=size)
    v = values>0

    t=time.time()
    numpy.apply_along_axis(find1,-1,v,1)
    print('find1',time.time()-t)

    t=time.time()
    find2(v,1)
    print('find2',time.time()-t)

출력

(1, 100000000)
('find1', 0.25300002098083496)
('find2', 0.2780001163482666)
(10000, 10000)
('find1', 0.46200013160705566)
('find2', 0.27300000190734863)
(1000000, 100)
('find1', 20.98099994659424)
('find2', 0.3040001392364502)
(10000000, 10)
('find1', 206.7590000629425)
('find2', 0.4830000400543213)

즉, C로 작성된 발견은 이러한 접근 방식 중 하나보다 조금 더 빠릅니다.


이것은 어떤가요

import numpy as np
np.amin(np.where(array==item))

배열을 숨기고 방법을 list사용할 수 있습니다 index().

i = list(array).index(item)

내가 아는 한 이것은 C 방법입니다.

참고 URL : https://stackoverflow.com/questions/7632963/numpy-find-first-index-of-value-fast

반응형