it-swarm.com.de

Finden Sie den nächsten Wert im numpy-Array

Gibt es einen numpy-thonic Weg, z. Funktion, um den nächsten Wert in einem Array zu finden? 

Beispiel:

np.find_nearest( array, value )
251
Fookatchu
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
405
unutbu

WENNIhr Array sortiert ist und sehr groß ist, ist dies eine viel schnellere Lösung:

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]

Dies skaliert zu sehr großen Arrays. Sie können das Obige einfach ändern, um in der Methode zu sortieren, wenn Sie nicht davon ausgehen können, dass das Array bereits sortiert ist. Es ist ein Overkill für kleine Arrays, aber sobald sie groß sind, ist dies viel schneller.

55
Demitri

Bei einer geringfügigen Änderung funktioniert die obige Antwort mit Arrays beliebiger Dimension (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]

Oder als einzelne Zeile geschrieben:

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

Hier ist eine Erweiterung, um den nächsten Vektor in einem Array von Vektoren zu finden. 

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

Zusammenfassung der Antwort : Wenn eine sortierte array vorhanden ist, führt der Halbierungscode (unten angegeben) am schnellsten aus. ~ 100-1000 mal schneller für große Arrays und ~ 2-100 mal schneller für kleine Arrays. Es erfordert auch kein numpy. Wenn Sie eine unsortierte array haben, sollten Sie, wenn array groß ist, zuerst eine O (n logn) -Sortierung und dann eine Halbierung verwenden. Wenn array klein ist, scheint Methode 2 die schnellste zu sein.

Zuerst solltest du klarstellen, was du mit dem nächsten Wert meinst . Oft möchte man das Intervall in einer Abszisse, z. array = [0,0.7,2.1], Wert = 1,95, Antwort wäre idx = 1. Dies ist der Fall, von dem ich vermute, dass Sie ihn benötigen (andernfalls kann das Folgende mit einer Folge-Bedingungsanweisung sehr leicht geändert werden, sobald Sie das Intervall gefunden haben). Ich werde darauf hinweisen, dass der beste Weg, dies durchzuführen, mit der Halbierung ist (was ich zuerst darlegen werde - beachten Sie, dass es überhaupt kein numpy erfordert und schneller ist als die Verwendung von numpy-Funktionen, da sie redundante Operationen ausführen). Dann werde ich einen Timing-Vergleich mit den anderen Benutzern vornehmen, die hier vorgestellt werden.

Halbierung:

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

Nun definiere ich den Code aus den anderen Antworten, sie geben jeweils einen Index zurück:

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

Jetzt werde ich mal die Codes eingeben: Hinweis Methoden 1,2,4,5 geben das Intervall nicht korrekt an. Die Methoden 1, 2, 4 werden auf den nächsten Punkt in der Anordnung gerundet (z. B.> = 1,5 -> 2), und das Verfahren 5 wird immer aufgerundet (z. B. 1,45 -> 2). Nur die Methoden 3 und 6 und natürlich die Halbierung geben das Intervall richtig an.

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

Bei einem großen Array ergibt die Halbierung 4us im Vergleich zu den nächstbesten 180us und längsten 1,21 ms (~ 100 - 1000 mal schneller). Für kleinere Arrays ist es ca. 2-100-mal schneller.

15
Josh Albert

Wenn Sie nicht numpy verwenden möchten, wird dies gemacht:

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

Hier ist eine Version mit scipy für @Ari Onasafari, antworten Sie mit ", um den nächsten Vektor in einem Array von Vektoren" zu finden.

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

Hier ist eine Version, die ein nicht-skalares "Werte" -Array behandelt:

import numpy as np

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

Oder eine Version, die einen numerischen Typ zurückgibt (z. B. int, float), wenn die Eingabe skalar ist:

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]
8
ryggyr

Bei großen Arrays ist die von @Demitri gegebene (ausgezeichnete) Antwort weitaus schneller als die derzeit als am besten bezeichnete Antwort. Ich habe seinen genauen Algorithmus auf zwei Arten angepasst:

  1. Die folgende Funktion funktioniert unabhängig davon, ob das Eingabefeld sortiert ist oder nicht.

  2. Die folgende Funktion gibt den Index des Eingabefeldes zurück, der dem nächstgelegenen Wert entspricht, was etwas allgemeiner ist.

Beachten Sie, dass die folgende Funktion auch einen bestimmten Edge-Fall behandelt, der zu einem Fehler in der von @Demitri geschriebenen Originalfunktion führen würde. Ansonsten ist mein Algorithmus identisch mit seinem. 

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
6
aph

Hier ist eine schnelle vektorisierte Version von @Dimitris Lösung, wenn Sie viele values suchen (values kann ein mehrdimensionales Array sein):

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

Benchmarks

> 100-mal schneller als eine for-Schleife mit der Lösung von @ Demitri

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

Dies ist eine vektorisierte Version von unutbu's Antwort :

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
Zhanwen Chen

Alle Antworten sind nützlich, um Informationen zu sammeln, um effizienten Code zu schreiben. Ich habe jedoch ein kleines Python-Skript geschrieben, um es für verschiedene Fälle zu optimieren. Es ist der beste Fall, wenn das bereitgestellte Array sortiert ist. Wenn man den Index des nächstgelegenen Punkts eines angegebenen Werts durchsucht, ist das Modul bisect am zeitsparendsten. Wenn bei einer Suche die Indizes einem Array entsprechen, ist der numpy searchsorted am effizientesten.

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)

In [63]:% time bisect.bisect_left (xlist, 0.3) CPU-Zeiten: Benutzer 0 ns, sys: 0 ns, Gesamt: 0 ns Wandzeit: 22,2 µs

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

In [64]:% time np.searchsorted (xar, 0.3, side = "left") CPU-Zeiten: Benutzer 0 ns, sys: 0 ns, Gesamt: 0 ns Wandzeit: 98,9 µs

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

% time np.searchsorted (xar, randpts, side = "left") CPU-Zeiten: Benutzer 4 ms, sys: 0 ns, Gesamt: 4 ms Wandzeit: 1,2 ms

Wenn wir der multiplikativen Regel folgen, sollte numpy ~ 100 ms dauern, was ~ 83X schneller ist.

1
Soumen

Ich denke, der pythonic Weg wäre:

 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)

Dies ist der grundlegende Code. Sie können es als Funktion verwenden, wenn Sie möchten

1
Ishan Tomar

Vielleicht hilfreich für ndarrays:

def find_nearest(X, value):
    return X[np.unravel_index(np.argmin(np.abs(X - value)), X.shape)]
0
Gusev Slava

Für ein 2d-Array, um die i, j-Position des nächsten Elements zu bestimmen:

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))
0
kareem mohamed