J'ai deux tableaux numpy 2d: x_array contient des informations de position dans la direction x, y_array contient des positions dans la direction y.
J'ai alors une longue liste de points x, y.
Pour chaque point de la liste, je dois trouver l'index de tableau de l'emplacement (spécifié dans les tableaux) qui est le plus proche de ce point.
J'ai naïvement produit du code qui fonctionne, basé sur cette question: Trouver la valeur la plus proche dans le tableau numpy
c'est à dire.
import time
import numpy
def find_index_of_nearest_xy(y_array, x_array, y_point, x_point):
distance = (y_array-y_point)**2 + (x_array-x_point)**2
idy,idx = numpy.where(distance==distance.min())
return idy[0],idx[0]
def do_all(y_array, x_array, points):
store = []
for i in xrange(points.shape[1]):
store.append(find_index_of_nearest_xy(y_array,x_array,points[0,i],points[1,i]))
return store
# Create some dummy data
y_array = numpy.random.random(10000).reshape(100,100)
x_array = numpy.random.random(10000).reshape(100,100)
points = numpy.random.random(10000).reshape(2,5000)
# Time how long it takes to run
start = time.time()
results = do_all(y_array, x_array, points)
end = time.time()
print 'Completed in: ',end-start
Je fais cela sur un grand ensemble de données et j'aimerais vraiment accélérer un peu. Quelqu'un peut-il optimiser cela?
Merci.
MISE À JOUR: SOLUTION suite aux suggestions de @silvado et @justin (ci-dessous)
# Shoe-horn existing data for entry into KDTree routines
combined_x_y_arrays = numpy.dstack([y_array.ravel(),x_array.ravel()])[0]
points_list = list(points.transpose())
def do_kdtree(combined_x_y_arrays,points):
mytree = scipy.spatial.cKDTree(combined_x_y_arrays)
dist, indexes = mytree.query(points)
return indexes
start = time.time()
results2 = do_kdtree(combined_x_y_arrays,points_list)
end = time.time()
print 'Completed in: ',end-start
Ce code ci-dessus a accéléré mon code (recherche de 5000 points dans des matrices 100x100) par 100 fois. Fait intéressant, l'utilisation de scipy.spatial.KDTree (au lieu de scipy.spatial.cKDTree) a donné un timing comparable à ma solution naïve, il vaut donc vraiment la peine d'utiliser la version cKDTree ...
scipy.spatial
a également une implémentation d'arborescence k-d: scipy.spatial.KDTree
.
L'approche consiste généralement à utiliser d'abord les données ponctuelles pour construire un arbre k-d. La complexité de calcul est de l'ordre de N log N, où N est le nombre de points de données. Les requêtes de plage et les recherches de voisins les plus proches peuvent alors être effectuées avec la complexité du log N. C'est beaucoup plus efficace que de simplement parcourir tous les points (complexité N).
Ainsi, si vous avez répété des requêtes de plage ou de voisin le plus proche, un arbre k-d est fortement recommandé.
Voici une scipy.spatial.KDTree
Exemple
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])
Si vous pouvez masser vos données dans le bon format, une solution rapide consiste à utiliser les méthodes de scipy.spatial.distance
:
http://docs.scipy.org/doc/scipy/reference/spatial.distance.html
En particulier, pdist
et cdist
fournissent des moyens rapides de calculer des distances par paires.