numpy 배열에서 가장 가까운 값 찾기


336

배열에서 가장 가까운 값 을 찾는 함수와 같은 수많은 방식이 있습니까?

예:

np.find_nearest( array, value )

답변:


516
import numpy as np
def find_nearest(array, value):
    array = np.asarray(array)
    idx = (np.abs(array - value)).argmin()
    return array[idx]

array = np.random.random(10)
print(array)
# [ 0.21069679  0.61290182  0.63425412  0.84635244  0.91599191  0.00213826
#   0.17104965  0.56874386  0.57319379  0.28719469]

value = 0.5

print(find_nearest(array, value))
# 0.568743859261

52
@ EOL : return np.abs(array-value).min()잘못된 대답을 제공합니다. 이를 통해 절대 값 거리의 최소값을 얻을 수 있으며 실제 배열 값을 반환해야합니다. 우리는 더해서 value가까이 다가올 수는 있지만 절대 값은 렌치를 사물에 던져 넣습니다.
unutbu

9
@ ~ unutbu 당신 말이 맞아요. 귀하의 솔루션보다 더 나은 것을 생각할 수 없습니다!
Eric O Lebigot

24
이 작업을 수행하는 numpy 내장 기능이 없다는 것이 미친 것 같습니다.
dbliss

3
@jsmedmar 이분법 (아래 답변 참조)은 O (log (n))입니다.
Josh Albert

4
FutureWarning: 'argmin' is deprecated. Use 'idxmin' instead. The behavior of 'argmin' will be corrected to return the positional minimum in the future. Use 'series.values.argmin' to get the position of the minimum now.위의 솔루션으로 나를 idxmin대신 사용 argmin하십시오. (v3.6.4)
jorijnsmit

78

경우 배열을 정렬하고 매우 큰되고, 이것은 훨씬 빠른 솔루션입니다 :

def find_nearest(array,value):
    idx = np.searchsorted(array, value, side="left")
    if idx > 0 and (idx == len(array) or math.fabs(value - array[idx-1]) < math.fabs(value - array[idx])):
        return array[idx-1]
    else:
        return array[idx]

이것은 매우 큰 배열로 확장됩니다. 배열이 이미 정렬되어 있다고 가정 할 수없는 경우 위의 방법으로 정렬하여 쉽게 정렬 할 수 있습니다. 작은 어레이에는 과잉이지만, 일단 커지면 훨씬 빠릅니다.


가장 합리적인 솔루션 인 것 같습니다. 어쨌든 왜 그렇게 느린 지 궁금합니다. np.searchsorted테스트 세트에 일반 은 약 2 µs, 전체 기능은 약 10 µs입니다. np.abs그것을 사용 하면 점점 악화되고 있습니다. 파이썬이 무엇을하고 있는지 전혀 알지 못합니다.
Michael

2
@Michael 단일 값의 경우 Numpy 수학 루틴이 math루틴 보다 느립니다 . 이 답변을 참조하십시오 .
Demitri

3
한 번에 여러 값을 조회하려는 경우 (몇 가지 조정만으로) 가장 좋은 솔루션입니다. 전체 if/else를 다음과 같이 교체해야합니다.idx = idx - (np.abs(value - array[idx-1]) < np.abs(value - array[idx])); return array[idx]
coderforlife

3
이것은 훌륭하지만 가장 큰 요소 value보다 크면 작동하지 않습니다 array. 나는 그 if진술이 if idx == len(array) or math.fabs(value - array[idx - 1]) < math.fabs(value - array[idx])나를 위해 작동하도록 변경했습니다 !
nicoco

3
idx가 0 일 때는 작동하지 않습니다. if는 다음과 같아야합니다.if idx > 0 and (idx == len(array) or math.fabs(value - array[idx-1]) < math.fabs(value - array[idx])):
JPaget

52

약간 수정하면 위의 대답은 임의의 차원 (1d, 2d, 3d, ...)의 배열에서 작동합니다.

def find_nearest(a, a0):
    "Element in nd array `a` closest to the scalar value `a0`"
    idx = np.abs(a - a0).argmin()
    return a.flat[idx]

또는 한 줄로 작성하십시오.

a.flat[np.abs(a - a0).argmin()]

6
"평평한"비트는 필요하지 않습니다. a[np.abs(a-a0).argmin)]잘 작동합니다.
Max Shron

2
실제로 argmin ()은 열 / 차원마다 여러 결과를 제공하기 때문에 여전히 하나의 차원에서만 작동합니다. 또한 나는 오타가 있었다. 이것은 적어도 2 차원에서 작동합니다 a[np.sum(np.square(np.abs(a-a0)),1).argmin()].
Max Shron

3
따라서 더 높은 차원에서는 작동하지 않으며 답을 삭제 (또는이를 반영하도록 수정)해야합니다.
Hugues Fontenelle

11
제안 된 답변이 효과가없는 예를 제공해주십시오. 하나를 찾으면 내 대답을 수정하겠습니다. 당신이 하나를 찾을 수 없다면 당신은 당신의 의견을 제거 할 수 있습니까?
kwgoodman

18

답변 요약 : 정렬 된 array경우 이분법 코드 (아래 제공)가 가장 빠릅니다. 대형 어레이의 경우 ~ 100-1000 배, 소형 어레이의 경우 ~ 2-100 배 더 빠릅니다. numpy도 필요하지 않습니다. 분류되지 않은 array경우 array큰 경우 먼저 O (n logn) 정렬을 사용한 다음 이분법을 고려해야하며, array작 으면 방법 2가 가장 빠릅니다.

먼저 가장 가까운 값으로 의미를 명확히해야합니다 . 가로축의 간격을 원할 때가 종종 있습니다 (예 : array = [0,0.7,2.1], value = 1.95, answer는 idx = 1입니다. 이것은 내가 필요하다고 생각하는 경우입니다 (그렇지 않으면 간격을 찾으면 후속 조건문을 사용하여 다음을 매우 쉽게 수정할 수 있습니다). 이 작업을 수행하는 최적의 방법은 이분법을 사용하는 것입니다 (먼저 제공 할 것입니다-전혀 numpy가 필요하지 않으며 중복 작업을 수행하기 때문에 numpy 함수를 사용하는 것보다 빠릅니다). 그런 다음 다른 사용자가 여기에 제시 한 다른 것과의 타이밍 비교를 제공합니다.

이등분:

def bisection(array,value):
    '''Given an ``array`` , and given a ``value`` , returns an index j such that ``value`` is between array[j]
    and array[j+1]. ``array`` must be monotonic increasing. j=-1 or j=len(array) is returned
    to indicate that ``value`` is out of range below and above respectively.'''
    n = len(array)
    if (value < array[0]):
        return -1
    elif (value > array[n-1]):
        return n
    jl = 0# Initialize lower
    ju = n-1# and upper limits.
    while (ju-jl > 1):# If we are not yet done,
        jm=(ju+jl) >> 1# compute a midpoint with a bitshift
        if (value >= array[jm]):
            jl=jm# and replace either the lower limit
        else:
            ju=jm# or the upper limit, as appropriate.
        # Repeat until the test condition is satisfied.
    if (value == array[0]):# edge cases at bottom
        return 0
    elif (value == array[n-1]):# and top
        return n-1
    else:
        return jl

이제 다른 답변에서 코드를 정의하고 각각 인덱스를 반환합니다.

import math
import numpy as np

def find_nearest1(array,value):
    idx,val = min(enumerate(array), key=lambda x: abs(x[1]-value))
    return idx

def find_nearest2(array, values):
    indices = np.abs(np.subtract.outer(array, values)).argmin(0)
    return indices

def find_nearest3(array, values):
    values = np.atleast_1d(values)
    indices = np.abs(np.int64(np.subtract.outer(array, values))).argmin(0)
    out = array[indices]
    return indices

def find_nearest4(array,value):
    idx = (np.abs(array-value)).argmin()
    return idx


def find_nearest5(array, value):
    idx_sorted = np.argsort(array)
    sorted_array = np.array(array[idx_sorted])
    idx = np.searchsorted(sorted_array, value, side="left")
    if idx >= len(array):
        idx_nearest = idx_sorted[len(array)-1]
    elif idx == 0:
        idx_nearest = idx_sorted[0]
    else:
        if abs(value - sorted_array[idx-1]) < abs(value - sorted_array[idx]):
            idx_nearest = idx_sorted[idx-1]
        else:
            idx_nearest = idx_sorted[idx]
    return idx_nearest

def find_nearest6(array,value):
    xi = np.argmin(np.abs(np.ceil(array[None].T - value)),axis=0)
    return xi

이제 코드의 시간을 정하겠습니다. 참고 방법 1,2,4,5는 간격을 올바르게 지정하지 않습니다. 방법 1,2,4는 배열에서 가장 가까운 점으로 반올림하고 (예 :> = 1.5-> 2), 방법 5는 항상 반올림합니다 (예 : 1.45-> 2). 방법 3, 6 및 물론 이분법 만 구간을 적절하게 제공합니다.

array = np.arange(100000)
val = array[50000]+0.55
print( bisection(array,val))
%timeit bisection(array,val)
print( find_nearest1(array,val))
%timeit find_nearest1(array,val)
print( find_nearest2(array,val))
%timeit find_nearest2(array,val)
print( find_nearest3(array,val))
%timeit find_nearest3(array,val)
print( find_nearest4(array,val))
%timeit find_nearest4(array,val)
print( find_nearest5(array,val))
%timeit find_nearest5(array,val)
print( find_nearest6(array,val))
%timeit find_nearest6(array,val)

(50000, 50000)
100000 loops, best of 3: 4.4 µs per loop
50001
1 loop, best of 3: 180 ms per loop
50001
1000 loops, best of 3: 267 µs per loop
[50000]
1000 loops, best of 3: 390 µs per loop
50001
1000 loops, best of 3: 259 µs per loop
50001
1000 loops, best of 3: 1.21 ms per loop
[50000]
1000 loops, best of 3: 746 µs per loop

큰 배열의 이분법은 다음 최고 180us 및 가장 긴 1.21ms (~ 100-1000 배 더 빠름)에 비해 4us를 제공합니다. 더 작은 어레이의 경우 ~ 2-100 배 빠릅니다.


2
배열이 정렬되었다고 가정합니다. 누군가 배열을 정렬하지 않으려는 이유는 여러 가지가 있습니다. 예를 들어, 배열이 선 그래프에서 데이터 요소를 나타내는 경우.
user1917407

7
파이썬 표준 라이브러리는 이미 이분법 알고리즘을 구현하고 있습니다 : docs.python.org/3.6/library/bisect.html
Felix

" array작은 경우 방법 2가 가장 빠른 것 같습니다." @JoshAlbert는 얼마나 작습니까?
Mr.Zeus

2
가장 가까운 값을 찾지 못하고 다음으로 가장 낮은 값을 찾습니다.
endolith

@endolith는 bisect에만 해당됩니다.
Homero Esmeraldo

17

다음은 벡터 배열에서 가장 가까운 벡터를 찾는 확장 기능입니다.

import numpy as np

def find_nearest_vector(array, value):
  idx = np.array([np.linalg.norm(x+y) for (x,y) in array-value]).argmin()
  return array[idx]

A = np.random.random((10,2))*100
""" A = array([[ 34.19762933,  43.14534123],
   [ 48.79558706,  47.79243283],
   [ 38.42774411,  84.87155478],
   [ 63.64371943,  50.7722317 ],
   [ 73.56362857,  27.87895698],
   [ 96.67790593,  77.76150486],
   [ 68.86202147,  21.38735169],
   [  5.21796467,  59.17051276],
   [ 82.92389467,  99.90387851],
   [  6.76626539,  30.50661753]])"""
pt = [6, 30]  
print find_nearest_vector(A,pt)
# array([  6.76626539,  30.50661753])

파이썬 반복을 통해 값을 norm(..., axis=-1)추출하는 것보다 빠를 것이라고 생각 합니다 x,y. 또한 x,y스칼라가 있습니까? 그 다음 norm(x+y)부터 버그, 예를 들어, 거리가 (+1, -1)0으로 처리됩니다
CFH

이것은 나를 위해 일했다idx = np.array([np.linalg.norm(x+y) for (x,y) in abs(array-value)]).argmin()
ezchx

9

numpy를 사용하지 않으려면 다음을 수행하십시오.

def find_nearest(array, value):
    n = [abs(i-value) for i in array]
    idx = n.index(min(n))
    return array[idx]

9

스칼라 이외의 "값"배열을 처리하는 버전은 다음과 같습니다.

import numpy as np

def find_nearest(array, values):
    indices = np.abs(np.subtract.outer(array, values)).argmin(0)
    return array[indices]

또는 입력이 스칼라 인 경우 숫자 유형 (예 : int, float)을 반환하는 버전 :

def find_nearest(array, values):
    values = np.atleast_1d(values)
    indices = np.abs(np.subtract.outer(array, values)).argmin(0)
    out = array[indices]
    return out if len(out) > 1 else out[0]

좋은 대답은 전에는 outerufunc 방법을 사용한 적이 없으며 앞으로 더 많이 사용할 것입니다. 그런데 첫 번째 함수는를 반환해야합니다 array[indices].
Widjet

1
이 솔루션은 확장되지 않습니다. np.subtract.outer집중 정말 느리고 메모리의 경우 전체 외 - 매트릭스 생성 array및 / 또는 values매우 큰입니다.
anthonybell

8

다음은 @Ari Onasafari의 scipy 버전입니다. " 벡터 배열에서 가장 가까운 벡터를 찾으려면 "

In [1]: from scipy import spatial

In [2]: import numpy as np

In [3]: A = np.random.random((10,2))*100

In [4]: A
Out[4]:
array([[ 68.83402637,  38.07632221],
       [ 76.84704074,  24.9395109 ],
       [ 16.26715795,  98.52763827],
       [ 70.99411985,  67.31740151],
       [ 71.72452181,  24.13516764],
       [ 17.22707611,  20.65425362],
       [ 43.85122458,  21.50624882],
       [ 76.71987125,  44.95031274],
       [ 63.77341073,  78.87417774],
       [  8.45828909,  30.18426696]])

In [5]: pt = [6, 30]  # <-- the point to find

In [6]: A[spatial.KDTree(A).query(pt)[1]] # <-- the nearest point 
Out[6]: array([  8.45828909,  30.18426696])

#how it works!
In [7]: distance,index = spatial.KDTree(A).query(pt)

In [8]: distance # <-- The distances to the nearest neighbors
Out[8]: 2.4651855048258393

In [9]: index # <-- The locations of the neighbors
Out[9]: 9

#then 
In [10]: A[index]
Out[10]: array([  8.45828909,  30.18426696])

KDTree를 구축하는 것은 그러한 문제에 대한 오버 헤드입니다. 큰 배열에서 여러 쿼리를 작성 해야하는 경우가 아니라면 이러한 솔루션을 권장하지 않습니다 ... 그리고 각 쿼리마다 즉시 작성하는 대신 한 번 작성하여 재사용하는 것이 좋습니다.
Ben

8

values검색 할 것이 많은 경우 ( values다차원 배열 일 수 있음) @Dimitri 솔루션의 빠른 벡터화 된 버전은 다음과 같습니다 .

#`values` should be sorted
def get_closest(array, values):
    #make sure array is a numpy array
    array = np.array(array)

    # get insert positions
    idxs = np.searchsorted(array, values, side="left")

    # find indexes where previous index is closer
    prev_idx_is_less = ((idxs == len(array))|(np.fabs(values - array[np.maximum(idxs-1, 0)]) < np.fabs(values - array[np.minimum(idxs, len(array)-1)])))
    idxs[prev_idx_is_less] -= 1

    return array[idxs]

벤치 마크

for@Demitri의 솔루션으로 루프를 사용하는 것보다 100 배 빠름

>>> %timeit ar=get_closest(np.linspace(1, 1000, 100), np.random.randint(0, 1050, (1000, 1000)))
139 ms ± 4.04 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

>>> %timeit ar=[find_nearest(np.linspace(1, 1000, 100), value) for value in np.random.randint(0, 1050, 1000*1000)]
took 21.4 seconds

배열에 일정한 샘플링이있는 경우 훨씬 간단 해집니다. idx = np.searchsorted(array, values)그러면 idx[array[idx] - values>np.diff(array).mean()*0.5]-=1마지막으로return array[idx]
Sergey Antopolskiy가

7

큰 배열의 경우 @Demitri가 제공하는 (우수한) 답변이 현재 최고로 표시된 답변보다 훨씬 빠릅니다. 나는 그의 정확한 알고리즘을 다음 두 가지 방식으로 조정했습니다.

  1. 아래 함수는 입력 배열의 정렬 여부에 관계없이 작동합니다.

  2. 아래 함수 는 가장 일반적인 값에 해당하는 입력 배열 의 인덱스 를 반환합니다 .

아래 함수는 @Demitri가 작성한 원래 함수의 버그로 이어질 수있는 특정 경우를 처리합니다. 그렇지 않으면 내 알고리즘은 그의 알고리즘과 동일합니다.

def find_idx_nearest_val(array, value):
    idx_sorted = np.argsort(array)
    sorted_array = np.array(array[idx_sorted])
    idx = np.searchsorted(sorted_array, value, side="left")
    if idx >= len(array):
        idx_nearest = idx_sorted[len(array)-1]
    elif idx == 0:
        idx_nearest = idx_sorted[0]
    else:
        if abs(value - sorted_array[idx-1]) < abs(value - sorted_array[idx]):
            idx_nearest = idx_sorted[idx-1]
        else:
            idx_nearest = idx_sorted[idx]
    return idx_nearest

1
이것이 코드 최적화가 코드를 더보기 어렵고 어렵게 만드는 좋은 예라는 것을 지적 할 가치가 있습니다. @unutbu가 제공하는 답변은 속도가 더 중요하기 때문에 속도가 주요 관심사가 아닌 경우에 선호됩니다.
aph

@Michael의 답변이 표시되지 않습니다. 이 오류입니까 아니면 내가 장님입니까?
Fookatchu

아니, 당신은 장님이 아니에요, 나는 단지 문맹입니다 ;-) 내가 대답하고있는 @Demitri였습니다. 내 잘못이야. 방금 게시물을 수정했습니다. 감사!
aph

Demitri와 귀하의 답변이 서로 다릅니다. 어떤 아이디어? x = np.array([2038, 1758, 1721, 1637, 2097, 2047, 2205, 1787, 2287, 1940, 2311, 2054, 2406, 1471, 1460]). find_nearest(x, 1739.5)(첫 번째 Quantile에 가장 가까운 값)을 사용하면 ( 1637합리적) 및 1(버그?)를 얻습니다 .
PatrickT

3

이것은 unutbu의 답변을 벡터화 한 버전입니다 .

def find_nearest(array, values):
    array = np.asarray(array)

    # the last dim must be 1 to broadcast in (array - values) below.
    values = np.expand_dims(values, axis=-1) 

    indices = np.abs(array - values).argmin(axis=-1)

    return array[indices]


image = plt.imread('example_3_band_image.jpg')

print(image.shape) # should be (nrows, ncols, 3)

quantiles = np.linspace(0, 255, num=2 ** 2, dtype=np.uint8)

quantiled_image = find_nearest(quantiles, image)

print(quantiled_image.shape) # should be (nrows, ncols, 3)

2

가장 파이썬적인 방법은 다음과 같습니다.

 num = 65 # Input number
 array = n.random.random((10))*100 # Given array 
 nearest_idx = n.where(abs(array-num)==abs(array-num).min())[0] # If you want the index of the element of array (array) nearest to the the given number (num)
 nearest_val = array[abs(array-num)==abs(array-num).min()] # If you directly want the element of array (array) nearest to the given number (num)

이것이 기본 코드입니다. 원하는 경우 함수로 사용할 수 있습니다


2

모든 답변은 정보를 수집하여 효율적인 코드를 작성하는 데 도움이됩니다. 그러나 다양한 사례에 맞게 작은 Python 스크립트를 작성했습니다. 제공된 배열이 정렬 된 경우가 가장 좋습니다. 지정된 값의 가장 가까운 지점의 색인을 검색 bisect하면 가장 시간 효율적인 모듈입니다. 하나의 검색 인덱스가 배열에 해당 numpy searchsorted하면 가장 효율적입니다.

import numpy as np
import bisect
xarr = np.random.rand(int(1e7))

srt_ind = xarr.argsort()
xar = xarr.copy()[srt_ind]
xlist = xar.tolist()
bisect.bisect_left(xlist, 0.3)

[63]에서 : % time bisect.bisect_left (xlist, 0.3) CPU 시간 : 사용자 0ns, sys : 0ns, 총계 : 0ns 벽면 시간 : 22.2µs

np.searchsorted(xar, 0.3, side="left")

[64]에서 : % time np.searchsorted (xar, 0.3, side = "left") CPU 시간 : 사용자 0 ns, sys : 0 ns, 총계 : 0 ns 월 시간 : 98.9 µs

randpts = np.random.rand(1000)
np.searchsorted(xar, randpts, side="left")

% time np.searchsorted (xar, randpts, side = "left") CPU 시간 : 사용자 4ms, 시스템 : 0ns, 총계 : 4ms 월 시간 : 1.2ms

곱셈 규칙을 따르면 numpy는 ~ 100ms가 걸리므로 ~ 83X가 더 빠릅니다.


1

2D 배열의 경우 가장 가까운 요소의 i, j 위치를 결정하려면 다음을 수행하십시오.

import numpy as np
def find_nearest(a, a0):
    idx = (np.abs(a - a0)).argmin()
    w = a.shape[1]
    i = idx // w
    j = idx - i * w
    return a[i,j], i, j

0
import numpy as np
def find_nearest(array, value):
    array = np.array(array)
    z=np.abs(array-value)
    y= np.where(z == z.min())
    m=np.array(y)
    x=m[0,0]
    y=m[1,0]
    near_value=array[x,y]

    return near_value

array =np.array([[60,200,30],[3,30,50],[20,1,-50],[20,-500,11]])
print(array)
value = 0
print(find_nearest(array, value))

1
안녕하세요, Stack Overflow에 오신 것을 환영합니다. 좋은 답변을 작성하는 방법을 확인하십시오 . 질문의 맥락에서 당신이 한 일에 대한 간단한 설명을 해보십시오!
Tristo

0

아마도 도움이 될 것입니다 ndarrays:

def find_nearest(X, value):
    return X[np.unravel_index(np.argmin(np.abs(X - value)), X.shape)]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.