Numpy dizisinde en yakın değeri bulun


336

Bir dizideki en yakın değeri bulmak için örneğin, işlev gibi sayısal bir yöntem var mı ?

Misal:

np.find_nearest( array, value )

Yanıtlar:


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()yanlış cevap veriyor. Bu size mutlak değer mesafesinin min değerini verir ve bir şekilde gerçek dizi değerini döndürmemiz gerekir. Ekleyebilir valueve yaklaşabiliriz, ama mutlak değer şeylere bir İngiliz anahtarı atar ...
unutbu

9
Haklısın, benim hatam. Çözümünüzden daha iyi bir şey düşünemiyorum!
Eric O Lebigot

24
deli gibi görünüyor bunu yapan bir yerleşik yerleşik değildir.
dbliss

3
@jsmedmar İkiye bölme yöntemi (aşağıdaki cevabıma bakın) O (log (n)) şeklindedir.
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.Yukarıdaki çözüm ile benim için çalışıyor idxminyerine kullanmak argmin. (v3.6.4)
jorijnsmit

78

EĞER diziniz sıralanıp çok büyük olduğunu, bu çok daha hızlı bir çözümdür:

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]

Bu çok büyük dizilere ölçeklendirilir. Dizinin zaten sıralanmış olduğunu varsayamazsanız, yöntemi sıralamak için yukarıdakileri kolayca değiştirebilirsiniz. Küçük diziler için aşırı dolu, ancak büyüdüklerinde bu çok daha hızlı.


Kulağa en makul çözüm gibi geliyor. Neden bu kadar yavaş olduğunu merak ediyorum. Düz np.searchsorted, test setim için yaklaşık 2 µs alır, tüm fonksiyon yaklaşık 10 µs'dir. np.absBunu kullanmak daha da kötüye gidiyor. Python'un orada ne yaptığını bilmiyorum.
Michael

2
@Michael Tek değerler için, Numpy matematik rutinleri rutinlerden daha yavaş olacaktır math, bu cevaba bakınız .
Demitri

3
Aynı anda aramak istediğiniz birden fazla değeriniz varsa (birkaç ayar ile) bu en iyi çözümdür. Bütünün if/elsedeğiştirilmesi gerekiyoridx = idx - (np.abs(value - array[idx-1]) < np.abs(value - array[idx])); return array[idx]
coderforlife

3
Bu harika ama en büyük unsurdan valuedaha büyükse işe yaramaz array. İfadeyi benim için işe yarayacak ifşekilde değiştirdim if idx == len(array) or math.fabs(value - array[idx - 1]) < math.fabs(value - array[idx])!
nicoco

3
if idx > 0 and (idx == len(array) or math.fabs(value - array[idx-1]) < math.fabs(value - array[idx])):
İdx

52

Hafif bir değişiklikle, yukarıdaki cevap keyfi boyut dizileri (1d, 2d, 3d, ...) ile çalışır:

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]

Veya tek bir satır olarak yazılır:

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

6
"Düz" bit gerekli değildir. a[np.abs(a-a0).argmin)]iyi çalışıyor.
Max Shron

2
Aslında, argmin () sütun / boyut başına birden çok sonuç verdiğinden, bu yalnızca bir boyut için geçerlidir. Ayrıca bir yazım hatası vardı. Bu en azından 2 boyutları için, çalışır: a[np.sum(np.square(np.abs(a-a0)),1).argmin()].
Max Shron

3
Bu nedenle, daha yüksek boyutlar için çalışmaz ve cevap silinmelidir (veya bunu yansıtacak şekilde değiştirilmelidir)
Hugues Fontenelle

11
Lütfen önerilen cevabın işe yaramadığı bir örnek veriniz. Birini bulursam cevabımı değiştireceğim. Birini bulamazsanız yorumlarınızı kaldırabilir misiniz?
kwgoodman

18

Cevabın özeti : Eğer biri sıralanırsa array(aşağıda verilen) ikiye ayırma kodu en hızlı şekilde çalışır. Büyük diziler için ~ 100-1000 kat, küçük diziler için ~ 2-100 kat daha hızlı. Ayrıca numpy gerektirmez. Sıralanmamışsa , arrayeğer arraybüyükse, önce O (n logn) türünü ve sonra ikiye ayırmayı düşünmelisiniz ve eğer arrayküçükse, yöntem 2 en hızlı görünür.

İlk önce ne demek istediğinizi en yakın değerle açıklığa kavuşturmalısınız . Genellikle bir apsiste aralık ister, örneğin dizi = [0,0,7,2,1], değer = 1,95, cevap idx = 1 olur. Bu, ihtiyaç duyduğunuzdan şüphelendiğim durumdur (aksi takdirde, aralığı bulduktan sonra aşağıdaki koşullu ifadeyle kolayca değiştirilebilir). Bunu gerçekleştirmenin en iyi yolunun iki bölümlü olduğuna dikkat edeceğim (ilk önce sağlayacağım - hiç numpy gerektirmediğini ve gereksiz işlemleri gerçekleştirdikleri için numpy işlevlerini kullanmaktan daha hızlı olduğunu unutmayın). Sonra burada diğer kullanıcılar tarafından sunulan diğerlerine karşı bir zamanlama karşılaştırma sağlayacaktır.

İkiye bölme:

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

Şimdi diğer cevaplardan kodu tanımlayacağım, her biri bir dizin döndürür:

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

Şimdi kodları zamanlayacağım : Not 1 , 2, 4, 5 aralıkları doğru aralığı vermiyor. Yöntemler 1,2,4 dizideki en yakın noktaya yuvarlanır (örn.> = 1,5 -> 2) ve yöntem 5 her zaman yukarı yuvarlanır (örneğin 1,45 -> 2). Sadece yöntem 3 ve 6 ve tabii ki biseksiyon aralığı uygun şekilde verir.

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

Büyük bir dizi için bisection bir sonraki en iyi 180us ve en uzun 1.21ms (~ 100-1000 kat daha hızlı) ile karşılaştırıldığında 4us verir. Daha küçük diziler için ~ 2-100 kat daha hızlıdır.


2
Dizinin sıralandığını varsayıyorsunuz. Birinin diziyi sıralamak istememesinin birçok nedeni vardır: örneğin, dizi bir çizgi grafikteki veri noktalarını temsil ediyorsa.
user1917407

7
Python standart kütüphanesi, ikiye ayırma
Felix

"Küçükse array, yöntem 2 en hızlı görünüyor " dediğinde . @JoshAlbert ne kadar küçük demek istedin?
Mr.Zeus

2
Bu en yakın değeri bulamaz , bir sonraki en düşük değeri bulur.
endolit

@endolith sadece bisect için geçerlidir.
Homero Esmeraldo

17

İşte bir vektör dizisindeki en yakın vektörü bulmak için bir uzantı.

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])

Bence değerleri Python yinelemesinden norm(..., axis=-1)çıkarmaktan daha hızlı olmalı x,y. Ayrıca, x,yskaler burada mı? O zaman norm(x+y)bir hata, örneğin mesafe (+1, -1)0 olarak kabul edilecektir.
cfh

Bu benim için çalıştıidx = np.array([np.linalg.norm(x+y) for (x,y) in abs(array-value)]).argmin()
ezchx

9

Numpy'yi kullanmak istemiyorsanız, bunu yaparsınız:

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

9

İşte skaler olmayan bir "değerler" dizisini işleyecek bir sürüm:

import numpy as np

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

Veya giriş skaler ise sayısal tip (ör. İnt, float) döndüren bir sürüm:

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]

İyi cevap, daha önce hiç outerbir ufunc yöntemini kullanmadım, sanırım gelecekte daha fazla kullanacağım. İlk işlev array[indices]bu arada dönmelidir .
Widjet

1
Bu çözüm ölçeklenmemektedir. np.subtract.outergerçekten yavaş olan arrayve / veya valuesçok büyükse bellek yoğun olan tüm dış ürün matrisini üretecektir .
anthonybell

8

İşte @Ari Onasafari, yanıt için SciPy bir sürümü "olduğunu vektörlerin bir dizideki en yakın vektörü bulmak için "

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 oluşturmak böyle bir sorun için oldukça ek bir yüktür. Büyük bir dizi üzerinde birden fazla sorgu yapmak zorunda sürece ben böyle bir çözüm tavsiye etmem ... Ve sonra, her sorgu için anında oluşturmak yerine, bir kez oluşturmak ve yeniden kullanmak daha iyi olurdu.
Ben

8

Eğer aranacak çok şeyiniz varsa values( valuesçok boyutlu dizi olabilir) @ Dimitri'nin çözümünün hızlı bir vektörize edilmiş hali :

#`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]

Deneyler

> for@ Demitri çözümü ile bir döngü kullanmaktan 100 kat daha hızlı`

>>> %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

dizide sürekli örnekleme olması durumunda, daha da basitleşir: idx = np.searchsorted(array, values)o zaman: idx[array[idx] - values>np.diff(array).mean()*0.5]-=1ve son olarakreturn array[idx]
Sergey Antopolskiy

7

Büyük diziler için @Demitri tarafından verilen (mükemmel) cevap, şu anda en iyi olarak işaretlenen cevaptan çok daha hızlıdır. Tam algoritmasını aşağıdaki iki şekilde uyarladım:

  1. Aşağıdaki işlev, giriş dizisinin sıralanıp sıralanmadığına bakar.

  2. Aşağıdaki işlev , en genel değere karşılık gelen giriş dizisinin dizinini döndürür , bu da biraz daha geneldir.

Aşağıdaki işlevin, @Demitri tarafından yazılan özgün işlevde hataya yol açacak belirli bir kenar durumunu da ele aldığını unutmayın. Aksi takdirde algoritmam onunkiyle aynıdır.

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
Bunun, kod optimizasyonunun onu nasıl daha çirkin ve okumayı zorlaştırdığına dair harika bir örnek olduğunu belirtmek gerekir. @Unutbu tarafından verilen cevap, çok daha şeffaf olduğu için hızın büyük bir endişe olmadığı durumlarda (çok) tercih edilmelidir.
aph

@Michael tarafından verilen cevabı görmüyorum. Bu bir hata mı yoksa kör müyüm?
Fookatchu

Hayýr, kör deđilsin, ben sadece okuma yazma bilmiyorum ;-) Bu cevabý rifflediđim @Demitri idi. Benim hatam. Yazımı düzelttim. Teşekkürler!
aph

Demitri ve seninki ile farklı cevaplar alıyorum. Herhangi bir fikir? x = np.array([2038, 1758, 1721, 1637, 2097, 2047, 2205, 1787, 2287, 1940, 2311, 2054, 2406, 1471, 1460]). İle find_nearest(x, 1739.5)(ilk kantil için en yakın değer), 1637(makul) ve 1(hata?) Olsun.
PatrickT

3

Bu unutbu'nun cevabının vectorized bir versiyonudur :

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

En pitonik yol olacağını düşünüyorum:

 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)

Temel kod budur. İsterseniz bir işlev olarak kullanabilirsiniz


2

Tüm cevaplar verimli kod yazmak için bilgi toplamak için faydalıdır. Ancak, çeşitli durumlar için optimize etmek için küçük bir Python betiği yazdım. Sağlanan dizi sıralanırsa en iyi durum bu olacaktır. Belirli bir değerin en yakın noktasının dizininde arama yapılırsa, bisectmodül en çok zaman tasarrufu sağlar. Bir arama indeksler bir diziye karşılık geldiğinde numpy searchsorted, en verimlidir.

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] içinde:% time bisect.bisect_left (xlist, 0.3) CPU süreleri: kullanıcı 0 ns, sys: 0 ns, toplam: 0 ns Duvar süresi: 22.2 µs

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

[64]:% time np.searchsorted (xar, 0.3, side = "left") CPU süreleri: kullanıcı 0 ns, sys: 0 ns, toplam: 0 ns Duvar süresi: 98.9 µs

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

% time np.searchsorted (xar, randpts, side = "left") CPU süreleri: kullanıcı 4 ms, sys: 0 ns, toplam: 4 ms Duvar süresi: 1.2 ms

Çarpma kuralına uyursak, numpy ~ 100 ms sürmelidir, bu da ~ 83X'i daha hızlı ifade eder.


1

2d dizisi için, en yakın öğenin i, j konumunu belirlemek için:

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
Merhaba, Stack Overflow'a hoş geldiniz. Nasıl iyi bir cevap yazacağınızı kontrol edin . Soru bağlamında yaptıklarınızla ilgili kısa bir açıklama yapmayı deneyin!
Tristo

0

Belki aşağıdakiler için yararlı olabilir ndarrays:

def find_nearest(X, value):
    return X[np.unravel_index(np.argmin(np.abs(X - value)), X.shape)]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.