web-dev-qa-db-fra.com

Distance euclidienne minimale entre les points de deux tableaux Numpy différents, non compris dans

J'ai deux tableaux de coordonnées x -y, et je voudrais trouver la distance euclidienne minimale entre chaque point dans un tableau avec - tous les points dans l'autre tableau. Les tableaux ne sont pas nécessairement de la même taille. Par exemple:

xy1=numpy.array(
[[  243,  3173],
[  525,  2997]])

xy2=numpy.array(
[[ 682, 2644],
[ 277, 2651],
[ 396, 2640]])

Ma méthode actuelle parcourt chaque coordonnée xy dans xy1 et calcule les distances entre cette coordonnée et les autres coordonnées.

mindist=numpy.zeros(len(xy1))
minid=numpy.zeros(len(xy1))

for i,xy in enumerate(xy1):
    dists=numpy.sqrt(numpy.sum((xy-xy2)**2,axis=1))
    mindist[i],minid[i]=dists.min(),dists.argmin()

Existe-t-il un moyen d'éliminer la boucle for et de faire en quelque sorte des calculs élément par élément entre les deux tableaux? J'envisage de générer une matrice de distance pour laquelle je pourrais trouver l'élément minimum dans chaque ligne ou colonne.

Une autre façon de voir le problème. Dis que je concatène xy1 (longueur m) et xy2 (longueur p) dans xy (longueur n), et je stocke les longueurs des tableaux d'origine. Théoriquement, je devrais alors être capable de générer une matrice de distance n x n à partir des coordonnées à partir desquelles je peux saisir une sous-matrice m x p. Existe-t-il un moyen de générer efficacement cette sous-matrice?

40
fideli

(Des mois plus tard) scipy.spatial.distance.cdist( X, Y ) donne toutes les paires de distances, pour X et Y 2 dim, 3 dim ...
Il fait également 22 normes différentes, détaillées ici .

# cdist example: (nx,dim) (ny,dim) -> (nx,ny)

from __future__ import division
import sys
import numpy as np
from scipy.spatial.distance import cdist

#...............................................................................
dim = 10
nx = 1000
ny = 100
metric = "euclidean"
seed = 1

    # change these params in sh or ipython: run this.py dim=3 ...
for arg in sys.argv[1:]:
    exec( arg )
np.random.seed(seed)
np.set_printoptions( 2, threshold=100, edgeitems=10, suppress=True )

title = "%s  dim %d  nx %d  ny %d  metric %s" % (
        __file__, dim, nx, ny, metric )
print "\n", title

#...............................................................................
X = np.random.uniform( 0, 1, size=(nx,dim) )
Y = np.random.uniform( 0, 1, size=(ny,dim) )
dist = cdist( X, Y, metric=metric )  # -> (nx, ny) distances
#...............................................................................

print "scipy.spatial.distance.cdist: X %s Y %s -> %s" % (
        X.shape, Y.shape, dist.shape )
print "dist average %.3g +- %.2g" % (dist.mean(), dist.std())
print "check: dist[0,3] %.3g == cdist( [X[0]], [Y[3]] ) %.3g" % (
        dist[0,3], cdist( [X[0]], [Y[3]] ))


# (trivia: how do pairwise distances between uniform-random points in the unit cube
# depend on the metric ? With the right scaling, not much at all:
# L1 / dim      ~ .33 +- .2/sqrt dim
# L2 / sqrt dim ~ .4 +- .2/sqrt dim
# Lmax / 2      ~ .4 +- .2/sqrt dim
43
denis

Pour calculer la matrice des distances m par p, cela devrait fonctionner:

>>> def distances(xy1, xy2):
...   d0 = numpy.subtract.outer(xy1[:,0], xy2[:,0])
...   d1 = numpy.subtract.outer(xy1[:,1], xy2[:,1])
...   return numpy.hypot(d0, d1)

les .outer les appels font deux de ces matrices (de différences scalaires le long des deux axes), les .hypot appels les transforme en une matrice de même forme (de distances euclidiennes scalaires).

25
Alex Martelli

La réponse acceptée ne répond pas entièrement à la question, qui demande de trouver la distance minimale entre les deux ensembles de points, pas la distance entre chaque point dans les deux ensembles.

Bien qu'une solution simple à la question initiale consiste en effet à calculer la distance entre chaque paire et à trouver de manière appropriée la valeur minimale, cela n'est pas nécessaire si l'on n'est que intéressé par les distances minimales . Une solution beaucoup plus rapide existe pour ce dernier problème.

Toutes les solutions proposées ont un temps d'exécution qui évolue comme m*p = len(xy1)*len(xy2). C'est correct pour les petits ensembles de données, mais une solution optimale peut être écrite qui évolue comme m*log(p), produisant d'énormes économies pour les grands xy2 jeux de données.

Cette mise à l'échelle optimale du temps d'exécution peut être obtenue en utilisant scipy.spatial.cKDTree comme suit

import numpy as np
from scipy import spatial

xy1 = np.array(
    [[243,  3173],
     [525,  2997]])

xy2 = np.array(
    [[682, 2644],
     [277, 2651],
     [396, 2640]])

# This solution is optimal when xy2 is very large
tree = spatial.cKDTree(xy2)
mindist, minid = tree.query(xy1)
print(mindist)

# This solution by @denis is OK for small xy2
mindist = np.min(spatial.distance.cdist(xy1, xy2), axis=1)
print(mindist)

mindist est la distance minimale entre chaque point dans xy1 et l'ensemble des points dans xy2

7
divenex

Pour ce que vous essayez de faire:

dists = numpy.sqrt((xy1[:, 0, numpy.newaxis] - xy2[:, 0])**2 + (xy1[:, 1, numpy.newaxis - xy2[:, 1])**2)
mindist = numpy.min(dists, axis=1)
minid = numpy.argmin(dists, axis=1)

Edit: Au lieu d'appeler sqrt, de faire des carrés, etc., vous pouvez utiliser numpy.hypot:

dists = numpy.hypot(xy1[:, 0, numpy.newaxis]-xy2[:, 0], xy1[:, 1, numpy.newaxis]-xy2[:, 1])
5
Alok Singhal
import numpy as np
P = np.add.outer(np.sum(xy1**2, axis=1), np.sum(xy2**2, axis=1))
N = np.dot(xy1, xy2.T)
dists = np.sqrt(P - 2*N)
4
Maanasa Priya