Am două puncte în 3D:
(xa, ya, za)
(xb, yb, zb)
Și vreau să se calculeze distanța:
dist = sqrt((xa-xb)^2 + (ya-yb)^2 + (za-zb)^2)
Ce's cel mai bun mod de a face acest lucru cu NumPy, sau cu Python, în general? Am:
a = numpy.array((xa ,ya, za))
b = numpy.array((xb, yb, zb))
Folosi numpy.linalg.norm
:
dist = numpy.linalg.norm(a-b)
Teoria din Spatele acestei: cum se găsește în Introducere în data Mining
Acest lucru funcționează pentru că distanța Euclidiană este norma l2 și valoarea implicită a ord parametru în numpy.linalg.norma este de 2.
Pentru oricine este interesat în calcul de distanțe multiple la o dată, m-am'am făcut o mică comparație, folosind perfplot (un mic proiect de-al meu).
Primul sfat este de a organiza datele astfel încât tablouri au dimensiune (3, n)
(și C-contigue, evident). Dacă adăugarea se întâmplă în contiguă prima dimensiune, lucrurile sunt mai rapide, și nu't contează prea mult dacă utilizați sqrt-suma " cu " axa=0
, linalg.norma " cu " axa=0
, sau
python a_min_b = a - b numpy.sqrt(numpy.einsum('ij,ij->j', a_min_b, a_min_b))
care este, de o marjă ușoară, cea mai rapidă variantă. (Care de fapt este valabil doar pentru un rând la fel de bine.)
Variantele de unde ai suma peste cea de-a doua axă, axa=1`, toate sunt considerabil mai lente.
Cod pentru a reproduce complot:
``python import numpy import perfplot din scipy.spațiale import distanta
def linalg_norm(date): a, b = date[0] reveni numpy.linalg.norma(a - b, axa=1)
def linalg_norm_T(date): a, b = de date1 reveni numpy.linalg.norma(a - b, axa=0)
def sqrt_sum(date): a, b = date[0] reveni numpy.sqrt(numpy.sum((a - b) ** 2, axa=1))
def sqrt_sum_T(date): a, b = de date1 reveni numpy.sqrt(numpy.sum((a - b) ** 2, axa=0))
def scipy_distance(date): a, b = date[0] reveni listă(map(distanța.euclidiană, a, b))
def sqrt_einsum(date): a, b = date[0] a_min_b = a - b reveni numpy.sqrt(numpy.einsum("ij,ij->nu", a_min_b, a_min_b))
def sqrt_einsum_T(date): a, b = de date1 a_min_b = a - b reveni numpy.sqrt(numpy.einsum("ij,ij->j", a_min_b, a_min_b))
def setup(n): o = numpy.aleatoare.rand(n, 3) b = numpy.aleatoare.rand(n, 3) out0 = numpy.array([a, b]) out1 = numpy.array([o.T, b.T]) reveni out0, out1
perfplot.salva( "norma.png", setup=setup, n_range=[2 ** k pentru k în intervalul(22)], kernel=[ linalg_norm, linalg_norm_T, scipy_distance, sqrt_sum, sqrt_sum_T, sqrt_einsum, sqrt_einsum_T, ], logx=True, logy=True, xlabel="len(x), len(y)", ) ``
Un alt exemplu de această rezolvare a problemelor metoda:
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)
Vreau să prezinți răspunsul simplu cu diverse notite. np.linalg.norma va face, probabil, mai mult decât ai nevoie:
dist = numpy.linalg.norm(a-b)
În primul rând - această funcție este proiectat pentru a lucra pe o listă și întoarce toate valorile, de exemplu, pentru a compara distanta de la pA
la set de puncte sP
:
sP = set(points)
pA = point
distances = np.linalg.norm(sP - pA, ord=2, axis=1.) # 'distances' is a list
Amintiți-vă de mai multe lucruri:
Deci
def distance(pointA, pointB):
dist = np.linalg.norm(pointA - pointB)
return dist
e't la fel de nevinovat cum pare.
>>> 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
În primul rând - de fiecare dată când ne spunem, avem de-a face global de căutare pentru "np", un domeniu de căutare pentru "linalg" și un domeniu de căutare pentru "norma", și aeriene de doar asteptare funcția poate echivala cu zeci de python instrucțiuni.
În cele din urmă, am pierdut două operații pe pentru a stoca rezultatul și reîncărcați-o pentru a reveni...
Prima trecere la îmbunătățirea: asigurați-căutare mai rapidă, treceți la magazin
def distance(pointA, pointB, _norm=np.linalg.norm):
return _norm(pointA - pointB)
Vom obține mult mai simplificat:
>>> 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
Apelul funcției aeriene se ridică în continuare la ceva de lucru, deși. Și tu'll vreau să fac referință pentru a determina dacă s-ar putea fi mai bine faci singur calculele:
def distance(pointA, pointB):
return (
((pointA.x - pointB.x) ** 2) +
((pointA.y - pointB.y) ** 2) +
((pointA.z - pointB.z) ** 2)
) ** 0.5 # fast sqrt
Pe unele platforme, **0.5
este mai rapid decât matematica.sqrt`. Kilometraj dvs. poate varia.
**** Avansate de performanță note.
De ce calculezi distanta? Dacă singurul scop este de a afișa,
print("The target is %.2fm away" % (distance(a, b)))
muta de-a lungul. Dar dacă te're comparând distanțele, faci gama cecuri, etc., Am'd dori să adăugați ceva util de performanță observații.
Să luăm două cazuri: sortare de la distanță sau sacrificarea o listă a elementelor care îndeplinesc o serie de constrângeri.
# 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)
Primul lucru pe care trebuie să vă amintiți este că suntem folosind Pitagora pentru a calcula distanța (dist = sqrt(x^2 + y^2 + z^2)
) așa că ne-am're face o mulțime de sqrt
apeluri. Matematica 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
Pe scurt: până când ne-am de fapt nevoie de la distanță într-o unitate de X, mai degrabă decât X^2, putem elimina cea mai grea parte a calculelor.
# 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)
Mare, ambele funcții nu mai face nici scump rădăcini pătrate. Ca'll fi mult mai rapid. Putem îmbunătăți, de asemenea, in_range prin conversia de la un generator:
def in_range(origin, range, things):
range_sq = range**2
yield from (thing for thing in things
if distance_sq(origin, thing) <= range_sq)
Asta, mai ales ca are beneficii dacă faci ceva de genul:
if any(in_range(origin, max_dist, things)):
...
Dar dacă foarte următorul lucru pe care aveți de gând să facă necesită o distanță,
for nearby in in_range(origin, walking_distance, hotdog_stands):
print("%s %.2fm" % (nearby.name, distance(origin, nearby)))
ia în considerare cedarea tupluri:
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)
Acest lucru poate fi util mai ales dacă s-ar putea lanț serie de controale ('de a găsi lucruri care sunt aproape de X și în Nm Y', de't au pentru a calcula distanța din nou).
Dar ce zici dacă ne-am're în căutarea unui foarte mare lista de "lucruri" și anticipăm o mulțime de ei nu merita atentie?
Acolo este de fapt un foarte simplu de optimizare:
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
Dacă acest lucru este util va depinde de dimensiunea de 'lucruri'.
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 ...
Și din nou, ia în considerare cedarea dist_sq. Nostru hotdog exemplu, atunci devine:
# 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))
Începând Python 3.8, a [
matematica](https://docs.python.org/dev/library/math.html) modul direct oferă [
dist`](https://docs.python.org/dev/library/math.html#math.dist funcția), care returnează distanța euclidiană între două puncte (având în vedere ca tupluri sau liste de coordonate):
from math import dist
dist((1, 2, 6), (-2, 3, 2)) # 5.0990195135927845
Și dacă te're de lucru cu liste:
dist([1, 2, 6], [-2, 3, 2]) # 5.0990195135927845
Am găsit o 'dist' funcția în matplotlib.mlab, dar eu nu't cred că's destul de la îndemână.
Am'm a o posta aici doar pentru referință.
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)
Poți scădea vectori și apoi innerproduct.
Urmând exemplul tău,
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)
Este cod simplu și este ușor de înțeles.
Îmi place np.dot` (produs scalar):
a = numpy.array((xa,ya,za))
b = numpy.array((xb,yb,zb))
distance = (np.dot(a-b,a-b))**.5
Unul frumos-liner:
dist = numpy.linalg.norm(a-b)
Cu toate acestea, în cazul în care viteza este un motiv de îngrijorare mi-ar recomanda experimente pe masina ta. Am constatat că, folosind "matematica" biblioteca's sqrt " cu " * *
operator de piata este mult mai rapid pe masina mea decât o linie NumPy soluție.
Mi-am făcut teste folosind acest program simplu:
#!/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
Pe masina mea, math_calc_dist
ruleaza mult mai rapid decât numpy_calc_dist
: 1.5 secunde comparativ 23.5 secunde.
Pentru a obține o diferență măsurabilă între fastest_calc_dist " și " math_calc_dist
am avut de a sus TOTAL_LOCATIONS
la 6000. Apoi fastest_calc_dist
ia ~50 secunde timp math_calc_dist
ia ~60 de secunde.
De asemenea, puteți experimenta cu numpy.sqrt " și " numpy.pătrată, deși ambele au fost mai lentă decât "matematica" alternative pe masina mea.
Testele au fost rulate cu Python 2.6.6.
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)
Puteți utiliza cu ușurință formula
distance = np.sqrt(np.sum(np.square(a-b)))
care nu face de fapt nimic mai mult decât folosind Pitagora' teorema pentru a calcula distanța, prin adăugarea pătratelor Δx, Δy și Δz și înrădăcinare rezultatul.
Deoarece Python 3.8 "matematica" modulul include funcția de matematica.dist()`. Vezi aici https://docs.python.org/3.8/library/math.html#math.dist.
math.dist(p1, p2) returnează distanța Euclidiană între două puncte p1 și p2, de fiecare dat ca o secvență (sau iterable) de coordonate.
pyhton import math print( math.dist( (0,0), (1,1) )) # sqrt(2) -> 1.4142 print( math.dist( (0,0,0), (1,1,1) )) # sqrt(3) -> 1.7321
Găsi diferența a două matrici prima. Apoi, se aplică element înțelept multiplicare cu numpy's multiplica comanda. După apoi, găsi însumare a elementului înțelept înmulțit nouă matrice. În cele din urmă, găsi rădăcina pătrată din suma.
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