Antworten:
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
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.
Verwenden idxmin
statt argmin
funktioniert für mich mit der obigen Lösung. (v3.6.4)
Wenn Ihr Array sortiert 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 auf sehr große Arrays. Sie können das oben Gesagte leicht ändern, um es in der Methode zu sortieren, wenn Sie nicht davon ausgehen können, dass das Array bereits sortiert ist. Es ist übertrieben für kleine Arrays, aber sobald sie groß werden, ist dies viel schneller.
np.searchsorted
dauert ungefähr 2 µs für meinen Testsatz, die gesamte Funktion ca. 10 µs. Mit wird np.abs
es noch schlimmer. Keine Ahnung, was Python dort macht.
math
Routinen, siehe diese Antwort .
if/else
muss ersetzt werden durchidx = idx - (np.abs(value - array[idx-1]) < np.abs(value - array[idx])); return array[idx]
value
es größer als array
das größte Element ist. Ich habe die if
Aussage geändert if idx == len(array) or math.fabs(value - array[idx - 1]) < math.fabs(value - array[idx])
, damit sie für mich funktioniert!
if idx > 0 and (idx == len(array) or math.fabs(value - array[idx-1]) < math.fabs(value - array[idx])):
Mit geringfügigen Änderungen 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()]
a[np.abs(a-a0).argmin)]
funktioniert gut.
a[np.sum(np.square(np.abs(a-a0)),1).argmin()]
.
Zusammenfassung der Antwort : Wenn eine sortiert ist, array
ist der Halbierungscode (unten angegeben) am schnellsten. ~ 100-1000-mal schneller für große Arrays und ~ 2-100-mal schneller für kleine Arrays. Es erfordert auch keine Numpy. Wenn Sie eine unsortierte haben , sollten Sie, array
wenn sie array
groß ist, zuerst eine O (n logn) -Sortierung und dann eine Halbierung verwenden. Wenn sie array
klein ist, scheint Methode 2 die schnellste zu sein.
Zuerst sollten Sie klarstellen, was Sie unter dem nächsten Wert verstehen . Oft möchte man das Intervall auf einer Abszisse, zB Array = [0,0.7,2.1], Wert = 1,95, Antwort wäre idx = 1. Dies ist der Fall, den Sie vermutlich benötigen (andernfalls kann das Folgende sehr einfach mit einer bedingten Folgeanweisung geändert werden, sobald Sie das Intervall gefunden haben). Ich werde bemerken, dass der optimale Weg, dies durchzuführen, die Halbierung ist (die ich zuerst bereitstellen werde - beachten Sie, dass es überhaupt kein Numpy erfordert und schneller ist als die Verwendung von Numpy-Funktionen, weil sie redundante Operationen ausführen). Dann werde ich einen Zeitvergleich mit den anderen hier von anderen Benutzern präsentierten bereitstellen.
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
Jetzt 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 die Codes zeitlich festlegen: Beachten Sie, dass die Methoden 1,2,4,5 das Intervall nicht korrekt angeben. Die Methoden 1,2,4 runden auf den nächsten Punkt im Array (z. B.> = 1,5 -> 2), und Methode 5 rundet immer auf (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
Für ein großes Array ergibt die Halbierung 4us im Vergleich zu den nächstbesten 180us und den längsten 1,21 ms (~ 100 - 1000-mal schneller). Für kleinere Arrays ist es ~ 2-100 mal schneller.
array
es klein ist, scheint Methode 2 die schnellste zu sein." Wie klein meintest du @JoshAlbert?
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])
norm(..., axis=-1)
sollte schneller sein als das Extrahieren der x,y
Werte durch Python-Iteration. Auch x,y
Skalare sind hier? Dann norm(x+y)
ist ein Fehler, da z. B. die Entfernung (+1, -1)
als 0 behandelt wird.
idx = np.array([np.linalg.norm(x+y) for (x,y) in abs(array-value)]).argmin()
Wenn Sie numpy nicht verwenden möchten, geschieht dies wie folgt:
def find_nearest(array, value):
n = [abs(i-value) for i in array]
idx = n.index(min(n))
return array[idx]
Hier ist eine Version, die ein nicht skalares "Werte" -Array verarbeitet:
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]
outer
Methode eines Ufunc verwendet. Ich denke, ich werde sie in Zukunft häufiger anwenden. Die erste Funktion sollte array[indices]
übrigens zurückkehren.
np.subtract.outer
erzeugt die gesamte Außenproduktmatrix, die sehr langsam und speicherintensiv ist, wenn array
und / oder values
sehr groß ist.
Hier ist eine Version mit scipy für @Ari Onasafari, antworte " 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])
Hier ist eine schnelle vektorisierte Version von @ Dimitris Lösung, wenn Sie viele values
suchen müssen ( 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 die Verwendung einer for
Schleife mit @ Demitris Lösung`
>>> %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]
Bei großen Arrays ist die (ausgezeichnete) Antwort von @Demitri weitaus schneller als die derzeit als beste gekennzeichnete Antwort. Ich habe seinen genauen Algorithmus auf zwei Arten angepasst:
Die folgende Funktion funktioniert unabhängig davon, ob das Eingabearray sortiert ist oder nicht.
Die folgende Funktion gibt den Index des Eingabearrays zurück, der dem nächsten Wert entspricht, der etwas allgemeiner ist.
Beachten Sie, dass die folgende Funktion auch einen bestimmten Randfall behandelt, der zu einem Fehler in der ursprünglichen Funktion von @Demitri 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
x = np.array([2038, 1758, 1721, 1637, 2097, 2047, 2205, 1787, 2287, 1940, 2311, 2054, 2406, 1471, 1460])
. Mit find_nearest(x, 1739.5)
(dem ersten Quantil am nächsten) erhalte ich 1637
(vernünftig) und 1
(Fehler?).
Dies ist eine vektorisierte Version der Antwort von 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)
Ich denke, der pythonischste 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 Grundcode. Sie können es als Funktion verwenden, wenn Sie möchten
Alle Antworten sind nützlich, um die Informationen zum Schreiben von effizientem Code zu sammeln. Ich habe jedoch ein kleines Python-Skript geschrieben, um es für verschiedene Fälle zu optimieren. Dies ist der beste Fall, wenn das bereitgestellte Array sortiert ist. Wenn man den Index des nächsten Punktes eines bestimmten Wertes durchsucht, bisect
ist das Modul am zeiteffizientesten. Wenn man die Indizes durchsucht, die einem Array entsprechen, numpy searchsorted
ist das 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]:% Zeit bisect.bisect_left (xlist, 0.3) CPU-Zeiten: Benutzer 0 ns, System: 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 bedeutet.
Für das 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
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))
Vielleicht hilfreich für ndarrays
:
def find_nearest(X, value):
return X[np.unravel_index(np.argmin(np.abs(X - value)), X.shape)]
return np.abs(array-value).min()
gibt die falsche Antwort. Dies gibt Ihnen die min der absoluten Wertentfernung, und irgendwie müssen wir den tatsächlichen Array-Wert zurückgeben. Wir könnten hinzufügenvalue
und