409 votes

trouver la valeur la plus proche dans un tableau numpy

existe-t-il un moyen numpy-thonique, par exemple une fonction, pour trouver la "valeur la plus proche" dans un tableau ? Exemple :

np.find_nearest( array, value )

Merci d'avance !

619voto

unutbu Points 222216
import numpy as np
def find_nearest(array,value):
    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

56 votes

@EOL : return np.abs(array-value).min() donne une mauvaise réponse. Cela vous donne le minimum de la distance de la valeur absolue, et d'une manière ou d'une autre nous devons retourner la valeur réelle du tableau. Nous pourrions ajouter value et s'en rapprochent, mais la valeur absolue jette un trouble dans les choses...

9 votes

@~unutbu Vous avez raison, c'est ma faute. Je n'ai rien trouvé de mieux que votre solution !

35 votes

C'est fou qu'il n'y ait pas un intégré numpy qui fasse ça.

97voto

Demitri Points 1315

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 math.fabs(value - array[idx-1]) < math.fabs(value - array[idx]):
        return array[idx-1]
    else:
        return array[idx]

Cela permet d'atteindre des réseaux de très grande taille. Vous pouvez facilement modifier la méthode ci-dessus pour trier dans la méthode si vous ne pouvez pas supposer que le tableau est déjà trié. C'est un peu exagéré pour les petits tableaux, mais une fois qu'ils sont grands, c'est beaucoup plus rapide.

1 votes

Cela semble être la solution la plus raisonnable. Je me demande pourquoi il est si lent de toute façon. Simple np.searchsorted prend environ 2 µs pour mon jeu de test, la fonction entière environ 10 µs. En utilisant np.abs ça devient encore pire. Aucune idée de ce que python fait là.

2 votes

@Michael Pour les valeurs uniques, les routines mathématiques de Numpy seront plus lentes que celles de l math routines, voir cette réponse .

5 votes

C'est la meilleure solution si vous souhaitez consulter plusieurs valeurs à la fois (avec quelques ajustements). L'ensemble if/else doit être remplacé par idx = idx - (np.abs(value - array[idx-1]) < np.abs(value - array[idx])); return array[idx]

70voto

kwgoodman Points 640

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

7 votes

La partie "plate" n'est pas nécessaire. a[np.abs(a-a0).argmin)] fonctionne bien.

2 votes

En fait, cela ne fonctionne toujours que pour une seule dimension, puisque argmin() donne plusieurs résultats par colonne / dimension. J'ai aussi fait une faute de frappe. Cela fonctionne, au moins pour 2 dimensions : a[np.sum(np.square(np.abs(a-a0)),1).argmin()] .

3 votes

Donc, cela ne fonctionne pas pour les dimensions supérieures, et la réponse devrait être supprimée (ou modifiée pour refléter cela)

19voto

Ari Onasafari Points 41

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

0 votes

Je pense norm(..., axis=-1) devrait être plus rapide que l'extraction du x,y par l'itération Python. Également, x,y sont des scalaires ici ? Alors norm(x+y) est un bug puisque, par exemple, la distance (+1, -1) sera traité comme 0.

0 votes

Cela a fonctionné pour moi idx = np.array([np.linalg.norm(x+y) for (x,y) in abs(array-value)]).argmin()

10voto

ryggyr Points 676

Voici une version qui gère 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[index]

Ou une version qui renvoie 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]

0 votes

Bonne réponse, je n'ai jamais utilisé le outer Je n'ai jamais utilisé la méthode de l'ufunc auparavant, mais je pense que je l'utiliserai davantage à l'avenir. La première fonction doit retourner array[indices] d'ailleurs.

2 votes

Cette solution n'est pas évolutive. np.subtract.outer générera la matrice entière du produit extérieur, ce qui est vraiment lent et gourmand en mémoire si array et/ou values est très grande.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X