파이썬에서 두 개의 n 차원 벡터 사이의 각도를 결정해야합니다. 예를 들어 입력은 다음과 같은 두 개의 목록이 될 수 있습니다. [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
답변
참고 : 두 벡터가 동일한 방향 (예 : (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에서 장황하거나 불투명 한 단순한 아이디어와 같은 용도로 동기를 부여 받았습니다.
답변
David Wolever의 솔루션 은 좋지만
부호있는 각도 를 원한다면 주어진 쌍이 오른 손잡이인지 왼손잡이인지 결정해야합니다 ( 추가 정보는 위키 참조 ).
이에 대한 내 해결책은 다음과 같습니다.
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
하지는 않지만 제 경우에는 잘 작동합니다. 이 동작은 수정 될 수 있지만 (주어진 쌍에 대해 손이 결정되기 때문에) 내가 원하고 작성해야하는 더 많은 코드가 필요합니다.
답변
sgt pepper의 훌륭한 답변 을 기반으로 정렬 된 벡터에 대한 지원 추가 및 Numba를 사용하여 2 배 이상의 속도 향상 추가
@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없는 결과
- 루프 당 359 µs ± 2.86 µs (7 회 실행의 평균 ± 표준 편차, 각 1000 루프)
그리고
- 루프 당 151 µs ± 820 ns (7 회 실행의 평균 ± 표준 편차, 각 10000 루프)