vectors two sklearn euclidean dist python numpy euclidean-distance

sklearn - euclidean distance two vectors python



Distancia euclídea mínima entre puntos en dos matrices Numpy diferentes, no dentro (5)

Tengo dos matrices de coordenadas x - y , y me gustaría encontrar la distancia euclidiana mínima entre cada punto en una matriz con todos los puntos en la otra matriz. Las matrices no son necesariamente del mismo tamaño. Por ejemplo:

xy1=numpy.array( [[ 243, 3173], [ 525, 2997]]) xy2=numpy.array( [[ 682, 2644], [ 277, 2651], [ 396, 2640]])

Mi método actual recorre cada coordenada xy en xy1 y calcula las distancias entre esa coordenada y las otras coordenadas.

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

¿Hay alguna forma de eliminar el bucle for y, de algún modo, hacer cálculos elemento por elemento entre las dos matrices? Me imagino que generaré una matriz de distancia para la cual podría encontrar el elemento mínimo en cada fila o columna.

Otra forma de ver el problema. Supongamos que concateno xy1 (longitud m ) y xy2 (longitud p ) en xy (longitud n ), y xy2 las longitudes de las matrices originales. Teóricamente, debería ser capaz de generar una matriz de distancia nxn a partir de esas coordenadas a partir de las cuales puedo tomar una submatriz mxp . ¿Hay alguna manera de generar eficientemente esta submatriz?


(Meses después) scipy.spatial.distance.cdist( X, Y ) da todos los pares de distancias, para X e Y 2 dim, 3 dim ...
También hace 22 normas diferentes, detalladas here .

# 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


La respuesta aceptada no aborda completamente la pregunta, que solicita encontrar la distancia mínima entre los dos conjuntos de puntos, no la distancia entre cada punto en los dos conjuntos.

A pesar de que una solución directa a la pregunta original de hecho consiste en calcular la distancia entre cada par y, por el momento, encontrar el mínimo, esto no es necesario si a uno solo le interesan las distancias mínimas . Existe una solución mucho más rápida para este último problema.

Todas las soluciones propuestas tienen un tiempo de ejecución que se escala como m*p = len(xy1)*len(xy2) . Esto está bien para conjuntos de datos pequeños, pero se puede escribir una solución óptima que se xy2 como m*log(p) , produciendo grandes ahorros para grandes xy2 datos xy2 .

Esta escala de tiempo de ejecución óptima se puede lograr utilizando scipy.spatial.cKDTree siguiente manera

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)

donde mindist es la distancia mínima entre cada punto en xy1 y el conjunto de puntos en xy2


Para calcular la matriz de distancias m por p, esto debería funcionar:

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

las llamadas de .outer forman dos matrices de este tipo (de las diferencias escalares a lo largo de los dos ejes), las llamadas .hypot convierten en una matriz de la misma forma (de distancias euclídeas escalares).


Por lo que estás tratando de hacer:

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)

Editar : en lugar de llamar a sqrt , hacer cuadrados, etc., puede usar numpy.hypot :

dists = numpy.hypot(xy1[:, 0, numpy.newaxis]-xy2[:, 0], xy1[:, 1, numpy.newaxis]-xy2[:, 1])


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)