numpy에 1D 배열이 있고 값이 numpy 배열의 값을 초과하는 인덱스의 위치를 찾고 싶습니다.
예 :
aa = range(-10,10)
aa
값 5
이 초과 되는 위치를 찾으십시오 .
numpy에 1D 배열이 있고 값이 numpy 배열의 값을 초과하는 인덱스의 위치를 찾고 싶습니다.
예 :
aa = range(-10,10)
aa
값 5
이 초과 되는 위치를 찾으십시오 .
답변:
이것은 조금 더 빠르며 더 멋지게 보입니다.
np.argmax(aa>5)
argmax
처음 부터 멈추 므로 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
argmax
처음에는 멈추지 않는 것 같습니다 True
. (이것은 True
다른 위치에 하나의 부울 배열을 만들어 테스트 할 수 있습니다 .) 속도는 아마도 argmax
출력 목록을 만들 필요가 없다는 사실에 의해 설명됩니다 .
argmax
.
aa
정렬 여부 , @Michael의 답변과 같이).
argmax
단일 천만 요소 부울 배열 True
을 실행하고 True
중요한 위치를 실행했습니다 . 따라서 1.11.2 argmax
는 부울 배열에서 "단락"하는 것 같습니다.
배열의 정렬 된 내용이 주어지면 더 빠른 방법이 있습니다 : searchsorted .
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
+1
와np.searchsorted(..., side='right')
side
정렬 된 배열에 반복되는 값이있는 경우에만 인수가 차이가 있다고 생각합니다 . 반환 된 인덱스의 의미는 변경하지 않습니다. 항상 쿼리 값을 삽입하고 다음 항목을 모두 오른쪽으로 이동하고 정렬 된 배열을 유지할 수있는 인덱스입니다.
side
동일한 값이 정렬 된 배열과 삽입 된 배열 모두에 있을 때 효과 가 있습니다. 정렬 된 배열에서 반복되는 값은 효과를 과장합니다 (변의 차이는 정렬 된 배열에 삽입되는 값이 나타나는 횟수입니다). side
않는 것이 그 인덱스의 정렬 된 배열에 값을 삽입하는 결과 배열을 변경하지 않지만, 반환 지수의 의미를 변경. 미묘하지만 중요한 차이점; 사실이 대답 N/2
은에없는 경우 잘못된 색인을 제공합니다 aa
.
N/2
에 없으면 하나에 의해 해제됩니다 aa
. 올바른 형태가 될 것이다 np.searchsorted(aa, N/2, side='right')
합니다 (없이 +1
). 그렇지 않으면 두 형식 모두 동일한 색인을 제공합니다. N
이상한 테스트 사례를 고려하십시오 ( N/2.0
파이썬 2를 사용하는 경우 강제로 플로팅해야 함).
나는 이것에 관심이 있었고 제안 된 모든 답변을 perfplot 과 비교했습니다 . (면책 조항 : 저는 perfplot의 저자입니다.)
당신은 당신을 통해 찾고있는 배열되는 것을 알고 있다면 이미 정렬 한 다음
numpy.searchsorted(a, alpha)
당신을위한 것입니다. 일정한 시간 작동입니다. 즉, 속도는 어레이의 크기에 의존 하지 않습니다 . 당신은 그것보다 빨리 얻을 수 없습니다.
배열에 대해 아무것도 모르면 문제가 없습니다.
numpy.argmax(a > alpha)
이미 정렬 :
분류되지 않은 :
줄거리를 재현하는 코드 :
import numpy
import perfplot
alpha = 0.5
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)
out = perfplot.show(
# 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, 20)],
logx=True,
logy=True,
xlabel='len(array)'
)
np.searchsorted
일정하지 않습니다. 실제로 O(log(n))
입니다. 그러나 테스트 사례는 실제로 최상의 사례 searchsorted
(즉 O(1)
)를 벤치마킹합니다 .
searchsorted
(또는 모든 알고리즘) O(log(n))
정렬 된 균일하게 분포 된 데이터에 대한 이진 검색보다 이길 수 있다고 의심 합니다. 편집 : searchsorted
입니다 이진 검색.
(A)의 경우 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 함수를 사용하는 것입니다.
@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 함수가 놀랍도록 빠르게 수행되지만 더 큰 배열의 경우 계산 함수 및 검색 정렬 함수보다 성능이 뛰어납니다.
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
예외를 throw합니다. 그러나 calculate
실제로 예외가 도움이되는 것을 말합니다.
즉, 예외 또는 유효하지 않은 반환 값을 포착하고 적어도 값이 배열에 있는지 확실하지 않은 경우 적절한 래퍼 함수로 이러한 호출을 래핑해야합니다.
참고 : 계산 및 searchsorted
옵션은 특수 조건에서만 작동합니다. "계산"기능을 사용하려면 일정한 단계가 필요하며 검색 정렬을 위해서는 배열을 정렬해야합니다. 따라서 올바른 환경에서 유용 할 수 있지만 이 문제에 대한 일반적인 해결책 은 아닙니다 . 정렬 된 파이썬 목록을 다루는 경우 Numpys 검색 정렬을 사용하는 대신 bisect 모듈을 살펴볼 수 있습니다.