Python中两个n维向量之间的角度

Pet*_*ter 64 python vector angle

我需要确定Python中两个n维向量之间的角度.例如,输入可以是两个列表,如下所示:[1,2,3,4][6,7,8,9].

Dav*_*ver 120

注意:如果两个向量具有相同的方向(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))
Run Code Online (Sandbox Code Playgroud)

  • angle_between((0, 0, 0), (0, 1, 0)) 将给出 nan 作为结果,而不是 90 (4认同)
  • 我的 numpy (version==1.12.1) 可以直接安全地使用 `arccos`。: In [140]: np.arccos(np.dot(np.array([1,0,0]),np.array([-1,0,0]))) Out[140]: 3.1415926535897931 In [ 141]:np.arccos(np.dot(np.array([1,0,0]),np.array([1,0,0])))输出[141]:0.0 (3认同)
  • 省略了至少一个输入向量是零向量的特殊情况,这对于"unit_vector"中的除法是有问题的.一种可能性是在这种情况下只返回此函数中的输入向量. (2认同)
  • @kafman 0向量的角度未定义(在数学中)。因此,它引发错误的事实是好的。 (2认同)

Ale*_*lli 55

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)))
Run Code Online (Sandbox Code Playgroud)

注意:当向量具有相同或相反的方向时,这将失败.正确的实现在这里:https://stackoverflow.com/a/13849249/71522

  • 鉴于`math.sqrt(x)`相当于`x**0.5`而``math.pow(x,y)`相当于`x**y`,我很惊讶这些幸存的冗余ax挥舞着在Python 2.x-> 3.0过渡期间.在实践中,我通常将这些数字事物作为更大的计算密集型过程的一部分,并且解释器支持'**'直接转到字节码BINARY_POWER,而不是'math'的查找,访问对于它的属性'sqrt',然后痛苦的慢字节码CALL_FUNCTION,可以在没有编码或可读性成本的情况下在速度上实现可测量的改进. (8认同)
  • 正如numpy的回答:如果舍入错误发挥作用,这可能会失败!这可能发生在并行和反平行向量上! (5认同)
  • 此外,如果你只需要cos,sin,tan角度而不是角度本身,那么你可以跳过math.acos获得余弦,并使用交叉积来获得正弦. (2认同)
  • 注意:**如果向量相同(例如,`angle((1.,1.,1.,(1.,1.,1.))`),这将失败**.有关更正确的版本,请参阅我的回答. (2认同)
  • 如果您正在讨论上面的实现,那么它会因为舍入错误而失败,而不是因为向量是并行的. (2认同)

Oli*_*ier 37

使用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
Run Code Online (Sandbox Code Playgroud)

  • 对于使用上面代码片段的人:`clip`应该被添加到numpy导入列表中. (4认同)
  • 由于舍入错误,最后一行可能导致错误.因此,如果你点(u,u)/ norm(u)**2它会导致1.0000000002然后arccos失败(对于反平行向量也是'工作') (3认同)
  • 注意:当两个向量的方向相同或相反时,这将失败**(屈服值“ nan”)。请参阅我的答案以获得更正确的版本。 (2认同)
  • 添加 Neo 的评论,最后一行应该是 `angle = arccos(clip(c, -1, 1))` 以避免舍入问题。这解决了@DavidWolever 的问题。 (2认同)

小智 23

另一种可能性是使用它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)
Run Code Online (Sandbox Code Playgroud)

这是输出:

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
Run Code Online (Sandbox Code Playgroud)

  • 这是最好的答案,因为它正是数学表达式theta = atan2(u ^ v,uv).这永远不会失败! (5认同)
  • 这是针对二维的。OP 要求 nD (3认同)

Kev*_*tel 8

找到两个向量之间的角度的简单方法(适用于 n 维向量),

Python代码:

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
Run Code Online (Sandbox Code Playgroud)


pau*_*kow 5

如果您正在使用 3D 矢量,您可以使用工具带vg简洁地完成此操作。它是 numpy 顶部的一个浅层。

import numpy as np
import vg

vec1 = np.array([1, 2, 3])
vec2 = np.array([7, 8, 9])

vg.angle(vec1, vec2)
Run Code Online (Sandbox Code Playgroud)

您还可以指定视角以通过投影计算角度:

vg.angle(vec1, vec2, look=vg.basis.z)
Run Code Online (Sandbox Code Playgroud)

或通过投影计算有符号角:

vg.signed_angle(vec1, vec2, look=vg.basis.z)
Run Code Online (Sandbox Code Playgroud)

我在上次创业时创建了这个库,它的动机是这样的用途:在 NumPy 中冗长或不透明的简单想法。


sgt*_*per 5

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))
Run Code Online (Sandbox Code Playgroud)

因此它并不完美,NotImplementedError但对于我的情况来说它效果很好。这种行为可以修复(因为任何给定的配对都确定了手感),但它需要我想要并且必须编写的更多代码。