web-dev-qa-db-fra.com

Trouver la valeur la plus proche dans le tableau numpy

Existe-t-il une méthode numpy-thonique, par exemple fonction, pour trouver la valeur la plus proche dans un tableau? 

Exemple:

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

SIvotre 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 varie en fonction de très grands tableaux. Vous pouvez facilement modifier ce qui précède pour trier 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.

55
Demitri

Avec une légère modification, la réponse ci-dessus fonctionne avec des tableaux de dimensions arbitraires (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()]
40
kwgoodman

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

Résumé de la réponse : Si l’on a une array triée, le code de bissection (indiqué ci-dessous) est le plus rapide. ~ 100-1000 fois plus rapide pour les baies de grande taille et ~ 2-100 fois plus vite pour les petites baies. Cela ne nécessite pas Numpy non plus. Si vous avez une array non triée, si array est grande, envisagez d’abord d’utiliser un tri O (n logn), puis une bissection, et si array est petit, la méthode 2 semble la plus rapide.

Vous devez d’abord préciser ce que vous entendez par valeur la plus proche . Souvent, on veut l'intervalle en abscisse, par ex. 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 ce qui suit peut être modifié très facilement avec une instruction conditionnelle de suivi une fois l’intervalle trouvé). Je noterai que la méthode optimale pour effectuer cette opération consiste à effectuer une bissection (ce que je vais fournir en premier - notez que cela ne nécessite pas numpy et est plus rapide que d’utiliser des fonctions numpy car elles effectuent des opérations redondantes). Ensuite, je fournirai une comparaison temporelle avec les autres présentés 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

Maintenant, je vais 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

Maintenant, je vais chronométrer les codes: Remarque les méthodes 1,2,4,5 ne donnent pas correctement l'intervalle. Méthodes 1, 2, 4 arrondir au point le plus proche du tableau (par exemple> = 1,5 -> 2) et méthode 5 toujours arrondir (par exemple 1,45 -> 2). Seules les méthodes 3 et 6, et bien sûr la bissection, donnent l’intervalle correctement.

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 une large matrice, la bissection donne 4us par rapport au meilleur suivant, soit 180us et la plus longue, 1,21 ms (environ 100 à 1000 fois plus rapide). Pour les baies plus petites, c'est environ 2 à 100 fois plus rapide.

15
Josh Albert

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]
9
Nick Crawford

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

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

Pour les tableaux de grande taille, l'excellente réponse donnée par @Demitri est beaucoup plus rapide que la réponse actuellement indiquée comme meilleure. J'ai adapté son algorithme exact des deux manières suivantes:

  1. La fonction ci-dessous fonctionne que le tableau en entrée soit trié ou non.

  2. La fonction ci-dessous renvoie le index du tableau en 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 Edge spécifique qui conduirait à un bogue dans la fonction originale é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
6
aph

Voici une version vectorisée rapide de la solution de @ Dimitri si vous avez plusieurs 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]

Benchmarks

> 100 fois plus rapide que d'utiliser une boucle for 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
3
anthonybell

Ceci est une version vectorisée de la réponse de 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)
2
Zhanwen Chen

Toutes les réponses sont bénéfiques pour rassembler les informations pour écrire du code efficace. Cependant, j'ai écrit un petit script Python à optimiser pour différents cas. Ce sera le meilleur cas si le tableau fourni est trié. Si l’on cherche l’index du point le plus proche d’une valeur spécifiée, alors le module bisect est celui qui prend le plus de temps. Quand on cherche les index correspondent à un tableau, le numpy searchsorted est le 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]:% time bisect.bisect_left (xlist, 0.3) Temps CPU: utilisateur 0 ns, sys: 0 ns, total: 0 ns Temps mural: 22,2 µs

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

Dans [64]:% temps np.searchsorted (xar, 0.3, side = "left") Temps CPU: utilisateur 0 ns, sys: 0 ns, total: 0 ns Temps de prise: 98,9 µs

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

% temps np.searchsorted (xar, randpts, side = "left") Temps CPU: utilisateur 4 ms, sys: 0 ns, total: 4 ms Temps de paroi: 1,2 ms

Si nous suivons la règle multiplicative, alors numpy devrait prendre environ 100 ms, ce qui implique environ 83X plus rapidement.

1
Soumen

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 une fonction si vous voulez

1
Ishan Tomar

Peut-être utile pour ndarrays:

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

Pour le 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
0
Eduardo S. Pereira
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