it-swarm.com.de

Wie kann die euklidische Entfernung mit NumPy berechnet werden?

Ich habe zwei Punkte in 3D:

(xa, ya, za)
(xb, yb, zb)

Und ich möchte die Entfernung berechnen:

dist = sqrt((xa-xb)^2 + (ya-yb)^2 + (za-zb)^2)

Was ist der beste Weg, dies mit NumPy oder allgemein mit Python zu tun? Ich habe:

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

Verwenden Sie numpy.linalg.norm :

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

In SciPy gibt es dafür eine Funktion. Es heißt euklidisch .

Beispiel:

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

Für alle, die mehrere Entfernungen gleichzeitig berechnen möchten, habe ich einen Vergleich mit perfplot (einem kleinen Projekt von mir) durchgeführt. Es stellt sich heraus, dass

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

berechnet die Abstände der Zeilen in a und b am schnellsten. Dies gilt eigentlich auch für nur eine Reihe!

 enter image description here


Code zum Reproduzieren des Diagramms:

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 list(map(distance.euclidean, a, b))


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


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


perfplot.show(
    setup=lambda n: numpy.random.Rand(2, n, 3),
    n_range=[2**k for k in range(20)],
    kernels=[linalg_norm, scipy_distance, mpl_dist, sqrt_sum, sqrt_einsum],
    logx=True,
    logy=True,
    xlabel='len(x), len(y)'
    )
66
Nico Schlömer

Eine andere Instanz von dieser Problemlösungsmethode :

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)
29
Nathan Fellman

Ich möchte auf die einfache Antwort mit verschiedenen Leistungshinweisen eingehen. np.linalg.norm wird vielleicht mehr tun, als Sie brauchen:

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

Erstens: Diese Funktion ist so konzipiert, dass sie über eine Liste arbeitet und alle Werte zurückgibt, z. um den Abstand von pA zur Punktmenge sP zu vergleichen:

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

Erinnern Sie sich an einige Dinge:

  • Python-Funktionsaufrufe sind teuer.
  • [Normal] Python speichert keine Namenssuche.

So

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

ist nicht so unschuldig wie es aussieht.

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

Erstens: Jedes Mal, wenn wir es aufrufen, müssen wir global nach "np" suchen, nach "linalg" suchen und nach "norm" suchen, und den Zusatzaufwand von lediglich call kann die Funktion gleichsetzen Dutzende von Python-Anweisungen.

Zum Schluss haben wir zwei Operationen verschwendet, um das Ergebnis zu speichern und für die Rückkehr neu zu laden ...

Erster Durchlauf bei Verbesserung: Machen Sie die Suche schneller, überspringen Sie den Speicher

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

Wir bekommen das viel schlanker:

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

Der Funktionsaufrufaufwand ist jedoch immer noch ein bisschen Arbeit. Und Sie sollten Benchmarks durchführen, um zu bestimmen, ob Sie vielleicht selbst besser rechnen:

def distance(pointA, pointB):
    return (
        ((pointA.x - pointB.x) ** 2) +
        ((pointA.y - pointB.y) ** 2) +
        ((pointA.z - pointB.z) ** 2)
    ) ** 0.5  # fast sqrt

Auf einigen Plattformen ist **0.5 schneller als math.sqrt. Ihre Laufleistung kann variieren.

**** Fortgeschrittene Leistungshinweise.

Warum berechnen Sie die Entfernung? Wenn der einzige Zweck darin besteht, es anzuzeigen,

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

weitergehen. Wenn Sie jedoch Entfernungen vergleichen, Entfernungsprüfungen durchführen usw., möchte ich einige nützliche Leistungsbeobachtungen hinzufügen.

Nehmen wir zwei Fälle: Sortierung nach Entfernung oder Auswahl einer Liste nach Elementen, die eine Bereichsbeschränkung erfüllen.

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

Als erstes müssen wir uns daran erinnern, dass wir Pythagoras verwenden, um die Entfernung (dist = sqrt(x^2 + y^2 + z^2)) zu berechnen, sodass wir viele sqrt-Aufrufe machen. 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

Kurzum: Bis wir tatsächlich den Abstand in einer Einheit von X anstelle von X ^ 2 benötigen, können wir den schwierigsten Teil der Berechnungen eliminieren.

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

Toll, beide Funktionen machen keine teuren Wurzeln mehr. Das geht viel schneller. Wir können in_range auch verbessern, indem wir es in einen Generator konvertieren:

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

Dies hat vor allem Vorteile, wenn Sie Folgendes tun:

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

Aber wenn das nächste, was Sie tun werden, eine Distanz erfordert,

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

betrachten Sie nachgebende Tupel:

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)

Dies kann besonders nützlich sein, wenn Sie Bereichsüberprüfungen durchführen ('Dinge in der Nähe von X und in Nm von Y' finden, da Sie die Entfernung nicht erneut berechnen müssen).

Aber wie sieht es aus, wenn wir eine wirklich große Liste von things suchen und wir davon ausgehen, dass viele von ihnen keine Überlegung wert sind?

Es gibt eigentlich eine sehr einfache Optimierung:

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

Ob dies nützlich ist, hängt von der Größe der "Dinge" ab.

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 calculate distance and range-check it ...

Und wieder erwägen Sie, dist_sq zu ergeben. Unser Hotdog-Beispiel wird dann:

# 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))
22
kfsone

Ich finde eine 'dist'-Funktion in matplotlib.mlab, aber ich denke nicht, dass sie praktisch genug ist. 

Ich poste es hier nur als Referenz.

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)
9
Alan Wang

Es kann wie folgt gemacht werden. Ich weiß nicht, wie schnell es ist, aber es wird kein NumPy verwendet.

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)))
8
The Demz

Ein schöner Ein-Liner:

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

Wenn Geschwindigkeit jedoch ein Problem ist, würde ich empfehlen, an Ihrer Maschine zu experimentieren. Ich habe festgestellt, dass die Verwendung der math-Bibliothek der sqrt-Bibliothek mit dem **-Operator für das Quadrat auf meinem Computer viel schneller ist als die einzeilige NumPy-Lösung.

Ich habe meine Tests mit diesem einfachen Programm durchgeführt:

#!/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

Auf meinem Rechner läuft math_calc_dist viel schneller als numpy_calc_dist: 1,5 Sekunden versus 23,5 Sekunden .

Um einen messbaren Unterschied zwischen fastest_calc_dist und math_calc_dist zu erhalten, musste TOTAL_LOCATIONS auf 6000 eingestellt werden. Dann dauert fastest_calc_dist ~ 50 Sekunden , während math_calc_dist ~ 60 Sekunden dauert.

Sie können auch mit numpy.sqrt und numpy.square experimentieren, obwohl beide langsamer waren als die math-Alternativen auf meinem Computer.

Meine Tests wurden mit Python 2.6.6 ausgeführt.

5
user118662

Sie können nur die Vektoren abziehen und dann das innere Produkt.

Ihrem Beispiel folgend,

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)

Es ist einfacher Code und leicht verständlich.

5
PuercoPop

Nachdem Sie a und b definiert haben, können Sie auch Folgendes verwenden:

distance = np.sqrt(np.sum((a-b)**2))
5
Alejandro Sazo

Ab Python 3.8 stellt das Modul math direkt die dist -Funktion bereit, die die euklidische Entfernung zwischen zwei Punkten (als Tupel mit Koordinaten angegeben) zurückgibt:

from python import math

dist((1, 2, 6), (-2, 3, 2)) # 5.0990195135927845

Wenn Sie mit Listen anstatt mit Tupeln arbeiten:

dist(Tuple([1, 2, 6]), Tuple([-2, 3, 2]))
3
Xavier Guihot

Ich mag np.dot (dot Produkt): 

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

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

Hier ist ein kurzer Code für die euklidische Entfernung in Python, wobei zwei Punkte als Listen in Python dargestellt wurden.

def distance(v1,v2): 
    return sum([(x-y)**2 for (x,y) in Zip(v1,v2)])**(0.5)
2
Andy Lee

Sie können die Formel leicht verwenden

distance = np.sqrt(np.sum(np.square(a-b)))

das macht eigentlich nichts anderes als die Verwendung des Satzes von Pythagoras zur Berechnung der Entfernung, indem die Quadrate von Δx, Δy und Δz addiert und das Ergebnis verwurzelt wird.

1

Berechnen Sie die euklidische Entfernung für den mehrdimensionalen Raum:

 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
1
Gennady Nikitin
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)
1
Ankur Nadda
import math

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

Finde zuerst den Unterschied von zwei Matrizen. Wenden Sie dann die elementweise Multiplikation mit dem multiplizierten Befehl von numpy an. Suchen Sie anschließend die Summation des Elements, und klicken Sie auf die neue Matrix. Finden Sie schließlich die Quadratwurzel der Summation.

def findEuclideanDistance(a, b):
    euclidean_distance = a - b
    euclidean_distance = np.sum(np.multiply(euclidean_distance, euclidean_distance))
    euclidean_distance = np.sqrt(euclidean_distance)
    return euclidean_distance
0
johncasey