Risposte:
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.
Utilizzando idxmin
invece di argmin
funziona per me con la soluzione sopra. (v3.6.4)
Se il tuo array è ordinato ed è molto grande, questa è una soluzione molto più veloce:
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]
Questo si adatta a matrici molto grandi. È possibile modificare facilmente quanto sopra per ordinare nel metodo se non si può presumere che l'array sia già ordinato. È eccessivo per gli array di piccole dimensioni, ma una volta ingranditi è molto più veloce.
np.searchsorted
richiede circa 2 µs per il mio set di test, l'intera funzione circa 10 µs. Usarlo np.abs
sta peggiorando ancora. Non ho idea di cosa ci faccia Python.
math
routine, vedi questa risposta .
if/else
deve essere sostituito conidx = idx - (np.abs(value - array[idx-1]) < np.abs(value - array[idx])); return array[idx]
value
è più grande del più array
grande elemento. Ho cambiato la if
dichiarazione per if idx == len(array) or math.fabs(value - array[idx - 1]) < math.fabs(value - array[idx])
farlo funzionare per me!
if idx > 0 and (idx == len(array) or math.fabs(value - array[idx-1]) < math.fabs(value - array[idx])):
Con una leggera modifica, la risposta sopra funziona con matrici di dimensione arbitraria (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]
Oppure, scritto come una riga singola:
a.flat[np.abs(a - a0).argmin()]
a[np.abs(a-a0).argmin)]
funziona bene.
a[np.sum(np.square(np.abs(a-a0)),1).argmin()]
.
Riepilogo della risposta : se si dispone di un ordinamento, array
il codice di sezione (indicato di seguito) esegue il più veloce. ~ 100-1000 volte più veloce per array di grandi dimensioni e ~ 2-100 volte più veloce per array di piccole dimensioni. Non richiede neanche intorpidimento. Se si dispone di un ordinamento non differenziato, array
se array
è grande, si dovrebbe considerare prima di utilizzare un ordinamento O (n logn) e quindi la bisection, e se array
è piccolo il metodo 2 sembra il più veloce.
Per prima cosa dovresti chiarire cosa intendi per valore più vicino . Spesso si desidera l'intervallo in un'ascissa, ad esempio array = [0,0.7,2.1], valore = 1.95, la risposta sarebbe idx = 1. Questo è il caso di cui sospetto sia necessario (altrimenti è possibile modificare molto facilmente quanto segue con un'istruzione condizionale di follow-up una volta trovato l'intervallo). Noterò che il modo ottimale per eseguire ciò è con la dissezione (che fornirò per prima cosa - nota che non richiede affatto intorpidimento ed è più veloce dell'uso delle funzioni intorpidite perché eseguono operazioni ridondanti). Quindi fornirò un confronto temporale con gli altri presentati qui da altri utenti.
Bisezione:
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
Ora definirò il codice dalle altre risposte, ognuna restituisce un indice:
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
Ora cronometrerò i codici: nota che i metodi 1,2,4,5 non danno correttamente l'intervallo. I metodi 1,2,4 arrotondano al punto più vicino dell'array (ad es.> = 1,5 -> 2) e il metodo 5 arrotonda sempre per eccesso (ad es. 1,45 -> 2). Solo i metodi 3 e 6 e, naturalmente, la bisection danno l'intervallo correttamente.
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
Per una vasta gamma bisection dà 4us rispetto ai successivi 180us migliori e 1,21ms più lunghi (~ 100 - 1000 volte più veloce). Per array più piccoli è ~ 2-100 volte più veloce.
array
è piccolo, il metodo 2 sembra il più veloce". quanto piccolo volevi dire @JoshAlbert?
Ecco un'estensione per trovare il vettore più vicino in una matrice di vettori.
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)
dovrebbe essere più veloce dell'estrazione dei x,y
valori tramite l'iterazione Python. Inoltre, gli x,y
scalari sono qui? Quindi norm(x+y)
è un bug poiché, ad esempio, la distanza (+1, -1)
sarà trattata come 0.
idx = np.array([np.linalg.norm(x+y) for (x,y) in abs(array-value)]).argmin()
Se non vuoi usare numpy questo lo farà:
def find_nearest(array, value):
n = [abs(i-value) for i in array]
idx = n.index(min(n))
return array[idx]
Ecco una versione che gestirà un array "valori" non scalare:
import numpy as np
def find_nearest(array, values):
indices = np.abs(np.subtract.outer(array, values)).argmin(0)
return array[indices]
O una versione che restituisce un tipo numerico (ad esempio int, float) se l'input è scalare:
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
metodo di un ufunc prima, penso che lo userò di più in futuro. A array[indices]
proposito, la prima funzione dovrebbe tornare .
np.subtract.outer
genererà l'intera matrice del prodotto esterno che è veramente lenta e richiede molta memoria se array
e / o values
è molto grande.
Ecco una versione con scipy per @Ari Onasafari, rispondi " per trovare il vettore più vicino in una serie di vettori "
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])
Ecco una versione vettoriale veloce della soluzione di @ Dimitri se ne hai molti values
da cercare ( values
può essere un array multidimensionale):
#`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]
Punti di riferimenti
> 100 volte più veloce rispetto all'uso di un for
loop con la soluzione di @ 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
idx = np.searchsorted(array, values)
quindi: idx[array[idx] - values>np.diff(array).mean()*0.5]-=1
e infinereturn array[idx]
Per array di grandi dimensioni, la risposta (eccellente) fornita da @Demitri è molto più rapida della risposta attualmente contrassegnata come migliore. Ho adattato il suo algoritmo esatto nei due modi seguenti:
La funzione seguente funziona indipendentemente dall'ordinamento dell'array di input.
La funzione seguente restituisce l' indice dell'array di input corrispondente al valore più vicino, che è in qualche modo più generale.
Si noti che la funzione seguente gestisce anche un caso limite specifico che porterebbe a un bug nella funzione originale scritta da @Demitri. Altrimenti, il mio algoritmo è identico al suo.
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])
. Con find_nearest(x, 1739.5)
(valore più vicino al primo quantile), ottengo 1637
(ragionevole) e 1
(bug?).
Questa è una versione vettoriale della risposta di 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)
Penso che il modo più pitonico sarebbe:
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)
Questo è il codice base. Puoi usarlo come una funzione se vuoi
Tutte le risposte sono utili per raccogliere le informazioni per scrivere un codice efficiente. Tuttavia, ho scritto un piccolo script Python da ottimizzare per vari casi. Sarà il caso migliore se viene ordinato l'array fornito. Se si cerca l'indice del punto più vicino di un valore specificato, il bisect
modulo è il più efficiente in termini di tempo. Quando una ricerca gli indici corrispondono a un array, numpy searchsorted
è il più efficiente.
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]:% tempo bisect.bisect_left (xlist, 0.3) tempi CPU: utente 0 ns, sys: 0 ns, totale: 0 ns Tempo muro: 22.2 µs
np.searchsorted(xar, 0.3, side="left")
In [64]:% tempo np.searchsorted (xar, 0.3, side = "left") Tempi CPU: utente 0 ns, sys: 0 ns, totale: 0 ns Tempo muro: 98,9 µs
randpts = np.random.rand(1000)
np.searchsorted(xar, randpts, side="left")
% tempo np.searchsorted (xar, randpts, side = "left") Tempi CPU: utente 4 ms, sys: 0 ns, totale: 4 ms Tempo muro: 1,2 ms
Se seguiamo la regola moltiplicativa, allora numpy dovrebbe richiedere ~ 100 ms, il che implica ~ 83 volte più veloce.
Per la matrice 2d, per determinare la posizione i, j dell'elemento più vicino:
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))
Forse utile per 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()
dà la risposta sbagliata. Questo ti dà il minimo della distanza del valore assoluto e in qualche modo dobbiamo restituire il valore effettivo dell'array. Potremmo aggiungerevalue
e avvicinarci, ma il valore assoluto getta una chiave inglese nelle cose ...