2009-09-09 33 views

Respuesta

464

Uso numpy.linalg.norm:

dist = numpy.linalg.norm(a-b) 
+2

Sabía que había una razón para que no aceptara mi propia respuesta :-). Solo para que conste, logré ver la respuesta de Mark Lavin antes de que la borrara. Me gustó más el enlace a los documentos de Python y la explicación. ¿Puedes agregar algunos detalles? –

+10

Los documentos linalg.norm se pueden encontrar aquí: http://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.norm.html Mi único comentario real fue el tipo de señalamiento de la conexión entre una norma (en este caso, la norma/2 norma de Frobenius, que es la predeterminada para la función de norma) y una métrica (en este caso, distancia euclídea). –

+3

Si OP desea calcular la distancia entre una matriz de coordenadas, también es posible usar [scipy.spatial.distance.cdist] (https://docs.scipy.org/doc/scipy/reference/generated/generated/scipy .spatial.distance.cdist.html). – mnky9800n

25

Otro ejemplo de this problem solving method. Tan pronto como me presenté la pregunta que tengo:

def dist(x,y): 
    return numpy.sqrt(numpy.sum((x-y)**2)) 

a = numpy.array((xa,ya,za)) 
b = numpy.array((xb,yb,zb)) 
dist_a_b = dist(a,b) 
+1

Se puede utilizar sqrt y/o implementaciones suma de numpy? Eso debería hacerlo más rápido (?). – u0b34a0f6ae

+1

Encontré esto en el otro lado de las interwebs 'norm = lambda x: N.sqrt (N.square (x) .sum())'; 'norm (x-y)' – u0b34a0f6ae

+1

rayar eso. Tenía que estar en algún lado. aquí está: 'numpy.linalg.norm (x-y)' – u0b34a0f6ae

6
dist = numpy.linalg.norm(a-b) 

es una bonita respuesta de una línea. Sin embargo, si la velocidad es una preocupación, recomendaría experimentar en su máquina. Descubrí que usar el math de la biblioteca sqrt con el operador ** para el cuadrado es mucho más rápido en mi máquina que la solución numpy de una sola línea.

Me pasé las pruebas que utilizan este sencillo programa:

#!/usr/bin/python 
import math 
import numpy 
from random import uniform 

def fastest_calc_dist(p1,p2): 
    return math.sqrt((p2[0] - p1[0]) ** 2 + 
        (p2[1] - p1[1]) ** 2 + 
        (p2[2] - p1[2]) ** 2)  

def math_calc_dist(p1,p2): 
    return math.sqrt(math.pow((p2[0] - p1[0]), 2) + 
        math.pow((p2[1] - p1[1]), 2) + 
        math.pow((p2[2] - p1[2]), 2)) 

def numpy_calc_dist(p1,p2): 
    return numpy.linalg.norm(numpy.array(p1)-numpy.array(p2)) 

TOTAL_LOCATIONS = 1000 

p1 = dict() 
p2 = dict() 
for i in range(0, TOTAL_LOCATIONS): 
    p1[i] = (uniform(0,1000),uniform(0,1000),uniform(0,1000)) 
    p2[i] = (uniform(0,1000),uniform(0,1000),uniform(0,1000)) 

total_dist = 0 
for i in range(0, TOTAL_LOCATIONS): 
    for j in range(0, TOTAL_LOCATIONS): 
     dist = fastest_calc_dist(p1[i], p2[j]) #change this line for testing 
     total_dist += dist 

print total_dist 

En mi máquina, math_calc_dist corre mucho más rápido que numpy_calc_dist: 1,5 segundos frente 23,5 segundos.

Para obtener una diferencia apreciable entre fastest_calc_dist y math_calc_dist tuviera que hasta TOTAL_LOCATIONS a 6000. Entonces fastest_calc_dist toma ~ 50 segundos, mientras quemath_calc_dist lleva ~ 60 segundos.

También puede experimentar con numpy.sqrt y numpy.square aunque ambos fueron más lentos que las alternativas math en mi máquina.

Mis pruebas se ejecutaron con Python 2.6.6.

+32

Está mal entendiendo cómo usar numpy ... _Don't_ use loops o list comprehensions. Si estás iterando, y aplicando la función a cada elemento, entonces, sí, las funciones numpy serán más lentas. El punto entero es vectorizar cosas. –

+0

Si muevo la llamada numpy.array al ciclo donde estoy creando los puntos obtengo mejores resultados con numpy_calc_dist, pero sigue siendo 10 veces más lenta que la quick_calc_dist. Si tengo tantos puntos y necesito encontrar la distancia entre cada par, no estoy seguro de qué más puedo hacer para aprovechar la ventaja. – user118662

+12

Me doy cuenta de que este hilo es antiguo, pero solo quiero reforzar lo que dijo Joe. No estás usando Numpy correctamente. Lo que está calculando es la suma de la distancia desde cada punto en p1 hasta cada punto en p2. La solución con numpy/scipy es más de 70 veces más rápida en mi máquina.Convierta p1 y p2 en una matriz (incluso utilizando un bucle si los tiene definidos como dicts). Luego puede obtener la suma total en un solo paso, 'scipy.spatial.distance.cdist (p1, p2) .sum()'. Eso es. –

3

Puede restar los vectores y luego el producto interno.

Siguiendo tu ejemplo

a = numpy.array((xa,ya,za)) 
b = numpy.array((xb,yb,zb)) 

tmp = a - b 
sum_squared = numpy.dot(tmp.T , tmp) 
result sqrt(sum_squared) 

código simple de una forma fácil de entender.

+4

esto me dará el cuadrado de la distancia. te falta un sqrt aquí. –

7

Puede hacerse así, no sé qué tan rápido es, pero no es numpy.

from math import sqrt 
a = (1,2,3) #data point 1 
b = (4,5,6) #data point 2 
print sqrt(sum((a - b)**2 for a, b in zip(a, b))) 
9

Encuentro una función 'dist' en matplotlib.mlab, pero no creo que sea lo suficientemente útil. Lo estoy publicando aquí solo como referencia.

import numpy as np 
import matplotlib as plt 
a = np.array([1,2,3]) 
b = np.array([2,3,4]) 
# distance between a and b 
dis = plt.mlab.dist(a,b) 
66

Hay una función para la que, en SciPy, se llama Euclidean

ejemplo:

from scipy.spatial import distance 
a = (1,2,3) 
b = (4,5,6) 
dst = distance.euclidean(a,b) 
+20

Si busca eficiencia, es mejor usar la función numpy. La distancia scipy es dos veces más lenta que numpy.linalg.norm (a-b) (y numpy.sqrt (numpy.sum ((a-b) ** 2))). En mi máquina consigo 19.7 μs con scipy (v0.15.1) y 8.9 μs con numpy (v1.9.2). No es una diferencia relevante en muchos casos, pero si en bucle puede ser más significativo. A partir de un vistazo rápido al código scipy, parece ser más lento porque valida la matriz antes de calcular la distancia. – Algold

+0

¿funciona esto en matrices numpy? –

+0

@MikePalmice sí, las funciones scipy son totalmente compatibles con numpy. Pero eche un vistazo a lo que aigold sugirió aquí (que también funciona en numpy array, por supuesto) – Avision

2

Aquí hay un código concisa para la distancia euclídea en Python dado dos puntos representados como listas en Python.

def distance(v1,v2): 
    return sum([(x-y)**2 for (x,y) in zip(v1,v2)])**(0.5) 
+1

Numpy también acepta listas como entradas (no es necesario pasar explícitamente una matriz numpy) –

3

me gusta np.dot (producto escalar):

a = numpy.array((xa,ya,za)) 
b = numpy.array((xb,yb,zb)) 

distance = (np.dot(a-b,a-b))**.5 
4

Tener a y b a medida que los definió, puede utilizar también:

distance = np.sqrt(np.sum((a-b)**2)) 
0

si usted quiere encontrar la distancia de un punto específico de la Primera de las contracciones que puede usar, además de que puede hacerlo con todas las dimensiones que desee.

import numpy as np 

A = [3,4] 
Dis = np.sqrt(A[0]**2 + A[1]**2) 
+0

su propuesta está limitada a dos matrices dimensionales. El objetivo es tener un código genérico que funcione en cualquier dimensión. –

0

calcular la distancia euclídea de espacio multidimensional

import math 

x = [1, 2, 6] 
y = [-2, 3, 2] 

dist = math.sqrt(sum([(xi-yi)**2 for xi,yi in zip(x,y)])) 
5.0990195135927845 
5

Quiero exponer sobre la respuesta sencilla con varias notas sobre los resultados. np.linalg.norm hará tal vez más de lo que necesita:

dist = numpy.linalg.norm(a-b) 

En primer lugar - esta función está diseñada para trabajar sobre una lista y devolver todos los valores, por ejemplo, para comparar la distancia de pA al conjunto de puntos sP:

sP = set(points) 
pA = point 
distances = np.linalg.norm(sP - pA, ord=2, axis=1.) # distances is a list 

Recuerde varias cosas: las llamadas a funciones

  • pitón son caros,
  • pitón [regulares] no almacena en caché las búsquedas de nombres.

Así:

def distance(pointA, pointB): 
    dist = np.linalg.norm(pointA - pointB) 
    return dist 

no es tan inocente como parece.

>>> dis.dis(distance) 
    2   0 LOAD_GLOBAL    0 (np) 
       2 LOAD_ATTR    1 (linalg) 
       4 LOAD_ATTR    2 (norm) 
       6 LOAD_FAST    0 (pointA) 
       8 LOAD_FAST    1 (pointB) 
      10 BINARY_SUBTRACT 
      12 CALL_FUNCTION   1 
      14 STORE_FAST    2 (dist) 

    3   16 LOAD_FAST    2 (dist) 
      18 RETURN_VALUE 

En primer lugar - cada vez que lo llamamos, tenemos que hacer una búsqueda global para "NP", una búsqueda con ámbito de "linalg" y una búsqueda con ámbito de "norma", y la sobrecarga de simplemente llamada la función puede equipararse a docenas de instrucciones de python.

Por último, desperdiciamos dos operaciones para almacenar el resultado y volver a cargarlo para devolver ...

primer paso en la mejora: realizar las operaciones de búsqueda más rápido, saltar la tienda

def distance(pointA, pointB, _norm=np.linalg.norm): 
    return _norm(pointA - pointB) 

tenemos la mucho más aerodinámico:

>>> dis.dis(distance) 
    2   0 LOAD_FAST    2 (_norm) 
       2 LOAD_FAST    0 (pointA) 
       4 LOAD_FAST    1 (pointB) 
       6 BINARY_SUBTRACT 
       8 CALL_FUNCTION   1 
      10 RETURN_VALUE 

La llamada sobrecarga de la función todavía asciende a un poco de trabajo, sin embargo. Y usted querrá hacer puntos de referencia para determinar si puede ser mejor hacer sus propios cálculos:

def distance(pointX, pointY): 
    return (
     ((pointX.x - pointY.x) ** 2) + 
     ((pointY.y - pointY.y) ** 2) + 
     ((pointZ.z - pointZ.z) ** 2) 
    ) ** 0.5 # fast sqrt 

En algunas plataformas, **0.5 es más rápido que math.sqrt. ymmv.

**** notas de rendimiento avanzadas.

¿Por qué calcula la distancia? Si el único propósito es mostrarlo:

print("The target is %.2fm away" % (distance(a, b))) 

muévete. Pero si comparas distancias, haces verificaciones de rango, etc., me gustaría agregar algunas observaciones de perforación útiles.

Tomemos dos casos: ordenando por distancia o seleccionando una lista para los artículos que cumplen con una restricción de rango.

# Ultra naive implementations. Hold onto your hat. 

def sort_things_by_distance(origin, things): 
    return things.sort(key=lambda thing: distance(origin, thing)) 

def in_range(origin, range, things): 
    things_in_range = [] 
    for thing in things: 
     if distance(origin, thing) <= range: 
      things_in_range.append(thing) 

Lo primero que tenemos que recordar es que estamos utilizando Pitágoras para calcular la distancia (dist = sqrt(x^2 + y^2 + z^2)) por lo que estamos haciendo un montón de llamadas sqrt. Math 101:

dist = root (x^2 + y^2 + z^2) 
:. 
dist^2 = x^2 + y^2 + z^2 
and 
sq(N) < sq(M) iff M > N 
and 
sq(N) > sq(M) iff N > M 
and 
sq(N) = sq(M) iff N == M 

En resumen: hasta que realmente requiere la distancia en una unidad de X en lugar de X^2, podemos eliminar la parte más difícil de los cálculos.

# Still naive, but much faster. 

def distance_sq(left, right): 
    """ Returns the square of the distance between left and right. """ 
    return (
     ((left.x - right.x) ** 2) + 
     ((left.y - right.y) ** 2) + 
     ((left.z - right.z) ** 2) 
    ) 

def sort_things_by_distance(origin, things): 
    return things.sort(key=lambda thing: distance_sq(origin, thing)) 

def in_range(origin, range, things): 
    things_in_range = [] 

    # Remember that sqrt(N)**2 == N, so if we square 
    # range, we don't need to root the distances. 
    range_sq = range**2 

    for thing in things: 
     if distance_sq(origin, thing) <= range_sq: 
      things_in_range.append(thing) 

Genial, ambas funciones ya no hacen ningún caro cuadrados. Eso será mucho más rápido. También podemos mejorar in_range mediante la conversión a un generador:

def in_range(origin, range, things): 
    range_sq = range**2 
    yield from (thing for thing in things 
       if distance_sq(origin, thing) <= range_sq) 

Este especial tiene beneficios si se está haciendo algo como:

if any(in_range(origin, max_dist, things)): 
    ... 

Pero si lo siguiente que se va a hacer requiere una distancia

for nearby in in_range(origin, walking_distance, hotdog_stands): 
    print("%s %.2fm" % (nearby.name, distance(origin, nearby))) 

consideran tuplas rendimiento:

def in_range_with_dist_sq(origin, range, things): 
    range_sq = range**2 
    for thing in things: 
     dist_sq = distance_sq(origin, thing) 
     if dist_sq <= range_sq: yield (thing, dist_sq) 

Esto puede ser especialmente útil si puede controlar la gama de cadenas ('busque cosas que estén cerca de X y dentro de Nm de Y', ya que no tiene que volver a calcular la distancia).

¿Pero qué pasa si estamos buscando una lista realmente grande de things y anticipamos que muchos de ellos no valen la pena?

realidad, hay una optimización muy simple:

def in_range_all_the_things(origin, range, things): 
    range_sq = range**2 
    for thing in things: 
     dist_sq = (origin.x - thing.x) ** 2 
     if dist_sq <= range_sq: 
      dist_sq += (origin.y - thing.y) ** 2 
      if dist_sq <= range_sq: 
       dist_sq += (origin.z - thing.z) ** 2 
       if dist_sq <= range_sq: 
        yield thing 

Si esto es útil dependerá del tamaño de las 'cosas'.

def in_range_all_the_things(origin, range, things): 
    range_sq = range**2 
    if len(things) >= 4096: 
     for thing in things: 
      dist_sq = (origin.x - thing.x) ** 2 
      if dist_sq <= range_sq: 
       dist_sq += (origin.y - thing.y) ** 2 
       if dist_sq <= range_sq: 
        dist_sq += (origin.z - thing.z) ** 2 
        if dist_sq <= range_sq: 
         yield thing 
    elif len(things) > 32: 
     for things in things: 
      dist_sq = (origin.x - thing.x) ** 2 
      if dist_sq <= range_sq: 
       dist_sq += (origin.y - thing.y) ** 2 + (origin.z - thing.z) ** 2 
       if dist_sq <= range_sq: 
        yield thing 
    else: 
     ... just calc distance and range-check it ... 

Y de nuevo, considere ceder el dist_sq. Nuestro ejemplo del perrito caliente se convierte en:

# chaining generators 
info = in_range_with_dist_sq(origin, walking_distance, hotdog_stands) 
info = (stand, dist_sq**0.5 for stand, dist_sq in info) 
for stand, dist in info: 
    print("%s %.2fm" % (stand, dist)) 
+1

¿Por qué no agregar una función tan optimizada a numpy? Una extensión para pandas también sería ideal para una pregunta como esta https://stackoverflow.com/questions/47643952/calculate-distance-based-on-a-lookup-dataframe – Keith

10

Para cualquier persona interesada en el cálculo de distancias múltiples a la vez, he hecho un poco de comparación utilizando perfplot (un pequeño proyecto mío). Resulta que

numpy.einsum('ij,ij->i', a-b, a-b) 

calcula las distancias de las filas en a y b más rápido. ¡Esto también es válido para una sola fila!

enter image description here


Código para reproducir la trama:

import matplotlib 
import numpy 
import perfplot 
from scipy.spatial import distance 


def linalg_norm(data): 
    a, b = data 
    return numpy.linalg.norm(a-b, axis=1) 


def sqrt_sum(data): 
    a, b = data 
    return numpy.sqrt(numpy.sum((a-b)**2, axis=1)) 


def scipy_distance(data): 
    a, b = data 
    return map(distance.euclidean, a, b) 


def mpl_dist(data): 
    a, b = data 
    return map(matplotlib.mlab.dist, a, b) 


def sqrt_einsum(data): 
    a, b = data 
    return numpy.einsum('ij,ij->i', a-b, a-b)**0.5 


perfplot.show(
    setup=lambda n: numpy.random.rand(2, n, 3), 
    n_range=[2**k for k in range(15)], 
    kernels=[linalg_norm, scipy_distance, mpl_dist, sqrt_sum, sqrt_einsum], 
    logx=True, 
    logy=True, 
    xlabel='len(x), len(y)' 
    ) 
0
import numpy as np 
from scipy.spatial import distance 
input_arr = np.array([[0,3,0],[2,0,0],[0,1,3],[0,1,2],[-1,0,1],[1,1,1]]) 
test_case = np.array([0,0,0]) 
dst=[] 
for i in range(0,6): 
    temp = distance.euclidean(test_case,input_arr[i]) 
    dst.append(temp) 
print(dst) 
+0

¿Cuál es la diferencia de [esta respuesta] (https: //stackoverflow.com/a/21986532/5376789)? – xskxzr

0
import math 

dist = math.hypot(math.hypot(xa-xb, ya-yb), za-zb) 
Cuestiones relacionadas