Подтвердить что ты не робот

Углы между двумя n-мерными векторами в Python

Мне нужно определить угол (s) между двумя n-мерными векторами в Python. Например, вход может состоять из двух списков: [1,2,3,4] и [6,7,8,9].

4b9b3361

Ответ 1

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

Примечание: это не удастся, если векторы имеют одинаковое или противоположное направление. Правильная реализация находится здесь: fooobar.com/questions/141806/...

Ответ 2

Примечание: все остальные ответы здесь потерпят неудачу, если два вектора имеют одно и то же направление (ex, (1, 0, 0), (1, 0, 0)) или противоположные направления (ex, (-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))

Ответ 3

Используя 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

Ответ 4

Другая возможность использует только 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

Ответ 5

Если вы работаете с трехмерными векторами, вы можете сделать это кратко, используя 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.

Ответ 6

Для тех немногих, кто мог (из-за сложностей с SEO) закончить здесь, пытаясь вычислить угол между двумя линиями в python, как в (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

Ответ 7

Использование 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)

Обратите внимание: эта функция генерирует исключение, если один из векторов имеет нулевую величину (деление на 0).

Ответ 8

Решение Дэвида Вулевер хорошо, но

Если вы хотите иметь углы со знаком, вы должны определить, является ли данная пара правой или левой рукой (см. Вики для получения дополнительной информации).

Мое решение для этого:

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 но для моего случая это работает хорошо. Это поведение может быть исправлено (потому что для любой данной пары определяется handness), но требуется больше кода, который я хочу и должен написать.