Pythonで2つのn次元ベクトル間の角度を決定する必要があります。たとえば、入力は次のような2つのリストにすることができます。[1,2,3,4]
および[6,7,8,9]
。
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)))
注:これは、ベクトルの方向が同じか反対の場合に失敗します。正しい実装はこちらです: https://stackoverflow.com/a/13849249/71522
注:2つのベクトルの方向が同じ場合(例:(1, 0, 0)
、(1, 0, 0)
)または反対方向(例:(-1, 0, 0)
、(1, 0, 0)
)。
これらのケースを正しく処理する関数を次に示します。
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))
numpy (強く推奨)を使用すると、次のようになります。
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
他の可能性は、numpy
だけを使用することです。
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)
出力は次のとおりです。
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
3Dベクトルを使用している場合は、toolbelt vg を使用してこれを簡潔に行うことができます。 numpyの上にある明るいレイヤーです。
import numpy as np
import vg
vec1 = np.array([1, 2, 3])
vec2 = np.array([7, 8, 9])
vg.angle(vec1, vec2)
表示角度を指定して、投影を介して角度を計算することもできます。
vg.angle(vec1, vec2, look=vg.basis.z)
または、投影を介して符号付き角度を計算します。
vg.signed_angle(vec1, vec2, look=vg.basis.z)
私は前回のスタートアップでこのライブラリを作成しましたが、NumPyで冗長または不透明なシンプルなアイデアのような使用法に動機付けられました。
(x0, y0), (x1, y1)
幾何学的な線のように、Pythonで2つのlinesの間の角度を計算しようとしてここで終了した可能性がある(SEOの合併症のため)少数の人には、以下があります最小解決策(shapely
モジュールを使用しますが、変更しないで簡単に変更できます):
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)
そして、使用は
>>> 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
David Woleverの解 は良いが、
署名された角度が必要な場合は、特定のペアが右利きか左利きかを判断する必要があります(詳細については wiki を参照してください)情報)。
これに対する私の解決策は次のとおりです。
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))
このNotImplementedError
のため完全ではありませんが、私の場合はうまく機能します。この動作は修正することができます(特定のペアに対してハンドネスが決定されるため).
Numpyを使用して、BandGapの丸めエラーを処理します。
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)
ベクトルの1つがゼロの大きさ(0で除算)の場合、この関数は例外をスローします。