Réponses:
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.
Utiliser idxmin
au lieu de argmin
fonctionne pour moi avec la solution ci-dessus. (v3.6.4)
SI votre tableau est trié et est très grand, c'est une solution beaucoup plus rapide:
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]
Cela se transforme en très grands tableaux. Vous pouvez facilement modifier ce qui précède pour trier dans la méthode si vous ne pouvez pas supposer que le tableau est déjà trié. C'est exagéré pour les petits tableaux, mais une fois qu'ils sont grands, c'est beaucoup plus rapide.
np.searchsorted
prend environ 2 µs pour mon ensemble de test, la fonction entière environ 10 µs. L'utilisation np.abs
devient encore pire. Aucune idée de ce que python fait là-bas.
math
routines, voir cette réponse .
if/else
doit être remplacé paridx = idx - (np.abs(value - array[idx-1]) < np.abs(value - array[idx])); return array[idx]
value
est plus grand que array
le plus gros élément. J'ai changé la if
déclaration pour la if idx == len(array) or math.fabs(value - array[idx - 1]) < math.fabs(value - array[idx])
faire fonctionner pour moi!
if idx > 0 and (idx == len(array) or math.fabs(value - array[idx-1]) < math.fabs(value - array[idx])):
Avec une légère modification, la réponse ci-dessus fonctionne avec des tableaux de dimension arbitraire (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]
Ou, écrit en une seule ligne:
a.flat[np.abs(a - a0).argmin()]
a[np.abs(a-a0).argmin)]
fonctionne bien.
a[np.sum(np.square(np.abs(a-a0)),1).argmin()]
.
Résumé de la réponse : si l'on a trié, array
le code de bissection (donné ci-dessous) est le plus rapide. ~ 100-1000 fois plus rapide pour les grandes baies, et ~ 2-100 fois plus rapide pour les petites baies. Il ne nécessite pas non plus numpy. Si vous avez un tri non trié, array
si if array
est grand, vous devez d' abord envisager d'utiliser un tri O (n logn), puis une bissection, et s'il array
est petit, la méthode 2 semble la plus rapide.
Vous devez d'abord clarifier ce que vous entendez par valeur la plus proche . Souvent, on veut l'intervalle en abscisse, par exemple tableau = [0,0.7,2.1], valeur = 1,95, la réponse serait idx = 1. C'est le cas dont je soupçonne que vous avez besoin (sinon les éléments suivants peuvent être modifiés très facilement avec une instruction conditionnelle de suivi une fois que vous avez trouvé l'intervalle). Je noterai que la manière optimale d'effectuer ceci est avec la bissection (que je fournirai en premier - notez qu'elle ne nécessite pas du tout numpy et est plus rapide que d'utiliser les fonctions numpy car elles effectuent des opérations redondantes). Ensuite, je fournirai une comparaison temporelle avec les autres présentées ici par d'autres utilisateurs.
Bissection:
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
Je vais maintenant définir le code des autres réponses, elles renvoient chacune un index:
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
Je vais maintenant chronométrer les codes: Notez que les méthodes 1, 2, 4, 5 ne donnent pas correctement l'intervalle. Les méthodes 1,2,4 arrondissent au point le plus proche dans le tableau (par exemple> = 1,5 -> 2), et la méthode 5 arrondit toujours (par exemple 1,45 -> 2). Seules les méthodes 3 et 6, et bien sûr la bissection donnent correctement l'intervalle.
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
Pour un grand tableau, la bissection donne 4us par rapport au 180us suivant et au plus long 1,21 ms (~ 100 - 1000 fois plus rapide). Pour les baies plus petites, c'est ~ 2-100 fois plus rapide.
array
c'est petit, la méthode 2 semble la plus rapide". à quel point vouliez-vous dire @JoshAlbert?
Voici une extension pour trouver le vecteur le plus proche dans un tableau de vecteurs.
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)
devrait être plus rapide que d'extraire les x,y
valeurs via l'itération Python. Aussi, x,y
les scalaires sont-ils ici? Il norm(x+y)
y a ensuite un bug puisque, par exemple, la distance (+1, -1)
sera traitée comme 0.
idx = np.array([np.linalg.norm(x+y) for (x,y) in abs(array-value)]).argmin()
Si vous ne voulez pas utiliser numpy, cela le fera:
def find_nearest(array, value):
n = [abs(i-value) for i in array]
idx = n.index(min(n))
return array[idx]
Voici une version qui gérera un tableau de "valeurs" non scalaire:
import numpy as np
def find_nearest(array, values):
indices = np.abs(np.subtract.outer(array, values)).argmin(0)
return array[indices]
Ou une version qui retourne un type numérique (par exemple int, float) si l'entrée est scalaire:
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
méthode d'un ufunc auparavant, je pense que je l'utiliserai plus à l'avenir. Soit dit array[indices]
en passant, la première fonction devrait revenir .
np.subtract.outer
générera toute la matrice du produit externe qui est vraiment lente et gourmande en mémoire si array
et / ou values
est très grande.
Voici une version avec scipy pour @Ari Onasafari, répondez " pour trouver le vecteur le plus proche dans un tableau de vecteurs "
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])
Voici une version vectorisée rapide de la solution de @ Dimitri si vous en avez beaucoup values
à rechercher ( values
peut être un tableau multidimensionnel):
#`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]
Repères
> 100 fois plus rapide que l'utilisation d'une for
boucle avec la solution de @ 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)
alors: idx[array[idx] - values>np.diff(array).mean()*0.5]-=1
et enfinreturn array[idx]
Pour les grands tableaux, la (excellente) réponse donnée par @Demitri est beaucoup plus rapide que la réponse actuellement indiquée comme la meilleure. J'ai adapté son algorithme exact des deux manières suivantes:
La fonction ci-dessous fonctionne que le tableau d'entrée soit trié ou non.
La fonction ci-dessous renvoie l' index du tableau d'entrée correspondant à la valeur la plus proche, ce qui est un peu plus général.
Notez que la fonction ci-dessous gère également un cas de bord spécifique qui entraînerait un bogue dans la fonction d'origine écrite par @Demitri. Sinon, mon algorithme est identique au sien.
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])
. Avec find_nearest(x, 1739.5)
(valeur la plus proche du premier quantile), j'obtiens 1637
(raisonnable) et 1
(bug?).
Ceci est une version vectorisée de la réponse d' 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)
Je pense que la manière la plus pythonique serait:
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)
Ceci est le code de base. Vous pouvez l'utiliser comme fonction si vous le souhaitez
Toutes les réponses sont utiles pour rassembler les informations pour écrire du code efficace. Cependant, j'ai écrit un petit script Python à optimiser pour divers cas. Ce sera le meilleur cas si le tableau fourni est trié. Si l'on recherche l'index du point le plus proche d'une valeur spécifiée, alors le bisect
module est le plus efficace en temps. Lorsqu'une recherche les indices correspondent à un tableau, le numpy searchsorted
plus efficace.
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)
Dans [63]:% temps bisect.bisect_left (xlist, 0,3) Temps CPU: utilisateur 0 ns, sys: 0 ns, total: 0 ns Temps de mur: 22,2 µs
np.searchsorted(xar, 0.3, side="left")
Dans [64]:% time np.searchsorted (xar, 0.3, side = "left") Temps CPU: utilisateur 0 ns, sys: 0 ns, total: 0 ns Temps de mur: 98,9 µs
randpts = np.random.rand(1000)
np.searchsorted(xar, randpts, side="left")
% time np.searchsorted (xar, randpts, side = "left") Temps CPU: utilisateur 4 ms, sys: 0 ns, total: 4 ms Temps de mur: 1,2 ms
Si nous suivons la règle multiplicative, alors numpy devrait prendre ~ 100 ms, ce qui implique ~ 83X plus rapide.
Pour un tableau 2d, pour déterminer la position i, j de l'élément le plus proche:
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))
Peut-être utile pour 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()
donne la mauvaise réponse. Cela vous donne le min de la distance de valeur absolue, et en quelque sorte nous devons retourner la valeur réelle du tableau. Nous pourrions ajoutervalue
et approcher, mais la valeur absolue jette une clé dans les choses ...