Python'da iki n boyutlu vektör arasındaki açıları belirlemem gerekiyor. Örneğin, giriş aşağıdaki gibi iki liste olabilir: [1,2,3,4]
ve [6,7,8,9]
.
Python'da iki n boyutlu vektör arasındaki açıları belirlemem gerekiyor. Örneğin, giriş aşağıdaki gibi iki liste olabilir: [1,2,3,4]
ve [6,7,8,9]
.
Yanıtlar:
import math
def dotproduct(v1, v2):
return sum((a*b) for a, b in zip(v1, v2))
def length(v):
return math.sqrt(dotproduct(v, v))
def angle(v1, v2):
return math.acos(dotproduct(v1, v2) / (length(v1) * length(v2)))
Not : Vektörler aynı veya zıt yöne sahip olduğunda bu başarısız olur. Doğru uygulama burada: https://stackoverflow.com/a/13849249/71522
math.sqrt(x)
eşdeğerdir x**0.5
ve math.pow(x,y)
eşdeğerdir x**y
, ben bu fazlalık balta Python 2.x-> 3.0 geçişi sırasında wielded hayatta şaşırttı. Pratikte, genellikle bu tür sayısal şeyleri daha büyük bir işlem yoğun sürecin parçası olarak yapıyorum ve yorumlayıcının '**' desteği doğrudan BINARY_POWER bayt koduna, 'matematik' araması yerine erişim 'sqrt' özniteliği ve ardından son derece yavaş olan CALL_FUNCTION bayt kodu, hiçbir kodlama veya okunabilirlik maliyeti olmaksızın hızda ölçülebilir bir gelişme sağlayabilir.
angle((1., 1., 1.), (1., 1., 1.))
. Biraz daha doğru bir versiyon için cevabıma bakın.
Not : İki vektör aynı yöne (ör. (1, 0, 0)
, (1, 0, 0)
) Veya zıt yönlere (ör . (-1, 0, 0)
, (1, 0, 0)
) Sahipse buradaki diğer yanıtların tümü başarısız olacaktır .
İşte bu durumları doğru bir şekilde ele alacak bir işlev:
import numpy as np
def unit_vector(vector):
""" Returns the unit vector of the vector. """
return vector / np.linalg.norm(vector)
def angle_between(v1, v2):
""" Returns the angle in radians between vectors 'v1' and 'v2'::
>>> angle_between((1, 0, 0), (0, 1, 0))
1.5707963267948966
>>> angle_between((1, 0, 0), (1, 0, 0))
0.0
>>> angle_between((1, 0, 0), (-1, 0, 0))
3.141592653589793
"""
v1_u = unit_vector(v1)
v2_u = unit_vector(v2)
return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))
np.isnan
Matematik kütüphanesindeki yerine kullanmak daha iyi olmaz mıydı ? Teoride aynı olmalılar, ancak pratikte pek emin değilim. Her iki durumda da daha güvenli olacağını düşünürdüm.
arccos
doğrudan ve güvenle kullanabilir . : Giriş [140]: np.arccos (np.dot (np.array ([1,0,0]), np.array ([- 1,0,0]))) Çıkış [140]: 3,1415926535897931 Giriş [ 141]: np.arccos (np.dot (np.array ([1,0,0]), np.array ([1,0,0]))) Çıkış [141]: 0,0
unit_vector
. Bir olasılık, bu durumda, bu fonksiyonda sadece giriş vektörünü döndürmektir.
Numpy kullanarak (şiddetle tavsiye edilir) şunları yaparsınız:
from numpy import (array, dot, arccos, clip)
from numpy.linalg import norm
u = array([1.,2,3,4])
v = ...
c = dot(u,v)/norm(u)/norm(v) # -> cosine of the angle
angle = arccos(clip(c, -1, 1)) # if you really want the angle
nan
). Daha doğru bir versiyon için cevabıma bakın.
angle = arccos(clip(c, -1, 1))
yuvarlama sorunlarından kaçınmak gerekir . Bu, @DavidWolever'ın sorununu çözer.
clip
numpy içe aktarmalar listesine eklenmelidir.
Diğer olasılık sadece kullanmaktır numpy
ve size iç açıyı verir
import numpy as np
p0 = [3.5, 6.7]
p1 = [7.9, 8.4]
p2 = [10.8, 4.8]
'''
compute angle (in degrees) for p0p1p2 corner
Inputs:
p0,p1,p2 - points in the form of [x,y]
'''
v0 = np.array(p0) - np.array(p1)
v1 = np.array(p2) - np.array(p1)
angle = np.math.atan2(np.linalg.det([v0,v1]),np.dot(v0,v1))
print np.degrees(angle)
ve işte çıktı:
In [2]: p0, p1, p2 = [3.5, 6.7], [7.9, 8.4], [10.8, 4.8]
In [3]: v0 = np.array(p0) - np.array(p1)
In [4]: v1 = np.array(p2) - np.array(p1)
In [5]: v0
Out[5]: array([-4.4, -1.7])
In [6]: v1
Out[6]: array([ 2.9, -3.6])
In [7]: angle = np.math.atan2(np.linalg.det([v0,v1]),np.dot(v0,v1))
In [8]: angle
Out[8]: 1.8802197318858924
In [9]: np.degrees(angle)
Out[9]: 107.72865519428085
3B vektörlerle çalışıyorsanız, bunu araç kemeri vg kullanarak kısaca yapabilirsiniz . Uyuşukluğun üstünde hafif bir tabakadır.
import numpy as np
import vg
vec1 = np.array([1, 2, 3])
vec2 = np.array([7, 8, 9])
vg.angle(vec1, vec2)
Açıyı yansıtma yoluyla hesaplamak için bir görüntüleme açısı da belirleyebilirsiniz:
vg.angle(vec1, vec2, look=vg.basis.z)
Veya işaret açısını projeksiyon yoluyla hesaplayın:
vg.signed_angle(vec1, vec2, look=vg.basis.z)
Kütüphaneyi son girişimde oluşturdum, bu tür kullanımlarla motive edildi: NumPy'de ayrıntılı veya opak olan basit fikirler.
David Wolever'ın çözümü iyidir, ancak
Eğer sahip olmak istiyorsanız imzalanan açıları verilen bir çifti haklı ya da (bkz teslim sol eğer belirlemek zorunda wiki ileri bilgi için).
Bunun için benim çözümüm:
def unit_vector(vector):
""" Returns the unit vector of the vector"""
return vector / np.linalg.norm(vector)
def angle(vector1, vector2):
""" Returns the angle in radians between given vectors"""
v1_u = unit_vector(vector1)
v2_u = unit_vector(vector2)
minor = np.linalg.det(
np.stack((v1_u[-2:], v2_u[-2:]))
)
if minor == 0:
raise NotImplementedError('Too odd vectors =(')
return np.sign(minor) * np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))
Bu NotImplementedError
yüzden mükemmel değil ama benim durumum için iyi çalışıyor. Bu davranış düzeltilebilir (çünkü herhangi bir çift için ellilik belirlenir) ancak istediğim ve yazmak zorunda olduğum daha fazla kod gerektirir.
İki vektör arasındaki açıyı bulmanın kolay yolu (n boyutlu vektör için çalışır),
Python kodu:
import numpy as np
vector1 = [1,0,0]
vector2 = [0,1,0]
unit_vector1 = vector1 / np.linalg.norm(vector1)
unit_vector2 = vector2 / np.linalg.norm(vector2)
dot_product = np.dot(unit_vector1, unit_vector2)
angle = np.arccos(dot_product) #angle in radian
SGT Biber'in harika cevabını temel almak ve hizalanmış vektörler için destek eklemek, ayrıca Numba kullanarak 2 kattan fazla hız artışı eklemek
@njit(cache=True, nogil=True)
def angle(vector1, vector2):
""" Returns the angle in radians between given vectors"""
v1_u = unit_vector(vector1)
v2_u = unit_vector(vector2)
minor = np.linalg.det(
np.stack((v1_u[-2:], v2_u[-2:]))
)
if minor == 0:
sign = 1
else:
sign = -np.sign(minor)
dot_p = np.dot(v1_u, v2_u)
dot_p = min(max(dot_p, -1.0), 1.0)
return sign * np.arccos(dot_p)
@njit(cache=True, nogil=True)
def unit_vector(vector):
""" Returns the unit vector of the vector. """
return vector / np.linalg.norm(vector)
def test_angle():
def npf(x):
return np.array(x, dtype=float)
assert np.isclose(angle(npf((1, 1)), npf((1, 0))), pi / 4)
assert np.isclose(angle(npf((1, 0)), npf((1, 1))), -pi / 4)
assert np.isclose(angle(npf((0, 1)), npf((1, 0))), pi / 2)
assert np.isclose(angle(npf((1, 0)), npf((0, 1))), -pi / 2)
assert np.isclose(angle(npf((1, 0)), npf((1, 0))), 0)
assert np.isclose(angle(npf((1, 0)), npf((-1, 0))), pi)
%%timeit
Numba içermeyen sonuçlar
Ve birlikte
Numpy kullanmak ve BandGap'in yuvarlama hatalarını dikkate almak:
from numpy.linalg import norm
from numpy import dot
import math
def angle_between(a,b):
arccosInput = dot(a,b)/norm(a)/norm(b)
arccosInput = 1.0 if arccosInput > 1.0 else arccosInput
arccosInput = -1.0 if arccosInput < -1.0 else arccosInput
return math.acos(arccosInput)
Bu fonksiyon, vektörlerden biri sıfır büyüklüğe sahipse (0'a bölün) bir istisna atacaktır.
Geometrik çizgilerde olduğu gibi python'daki iki çizgi arasındaki açıyı hesaplamaya çalışırken burada sona eren (SEO komplikasyonları nedeniyle) birkaç kişi (x0, y0), (x1, y1)
için, aşağıdaki minimum çözüm vardır ( shapely
modülü kullanır , ancak kolayca değiştirilemez):
from shapely.geometry import LineString
import numpy as np
ninety_degrees_rad = 90.0 * np.pi / 180.0
def angle_between(line1, line2):
coords_1 = line1.coords
coords_2 = line2.coords
line1_vertical = (coords_1[1][0] - coords_1[0][0]) == 0.0
line2_vertical = (coords_2[1][0] - coords_2[0][0]) == 0.0
# Vertical lines have undefined slope, but we know their angle in rads is = 90° * π/180
if line1_vertical and line2_vertical:
# Perpendicular vertical lines
return 0.0
if line1_vertical or line2_vertical:
# 90° - angle of non-vertical line
non_vertical_line = line2 if line1_vertical else line1
return abs((90.0 * np.pi / 180.0) - np.arctan(slope(non_vertical_line)))
m1 = slope(line1)
m2 = slope(line2)
return np.arctan((m1 - m2)/(1 + m1*m2))
def slope(line):
# Assignments made purely for readability. One could opt to just one-line return them
x0 = line.coords[0][0]
y0 = line.coords[0][1]
x1 = line.coords[1][0]
y1 = line.coords[1][1]
return (y1 - y0) / (x1 - x0)
Ve kullanım olurdu
>>> line1 = LineString([(0, 0), (0, 1)]) # vertical
>>> line2 = LineString([(0, 0), (1, 0)]) # horizontal
>>> angle_between(line1, line2)
1.5707963267948966
>>> np.degrees(angle_between(line1, line2))
90.0