Program Club

파이썬에서 두 개의 n 차원 벡터 사이의 각도

proclub 2020. 11. 25. 21:25
반응형

파이썬에서 두 개의 n 차원 벡터 사이의 각도


파이썬에서 두 개의 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에서 장황하거나 불투명 한 단순한 아이디어와 같은 용도로 동기를 부여 받았습니다.


(SEO의 합병증으로 인해) 기하학적 선 에서와 같이 파이썬에서 사이의 각도를 계산하려고 여기에서 끝났을 수있는 소수를 위해 다음 과 같은 최소 솔루션이 있습니다 ( 모듈을 사용 하지만 쉽게 수정할 수 없음).(x0, y0), (x1, y1)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

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)

Note, this function will throw an exception if one of the vectors has zero magnitude (divide by 0).


David Wolever's solution is good, but

If you want to have signed angles you have to determine if a given pair is right or left handed (see wiki for further info).

My solution for this is:

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

It's not perfect because of this NotImplementedError but for my case it works well. This behaviour could be fixed (cause handness is determined for any given pair) but it takes more code that I want and have to write.

참고URL : https://stackoverflow.com/questions/2827393/angles-between-two-n-dimensional-vectors-in-python

반응형