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

Python中两个n维向量之间的角度,python,vector,angle,Python,Vector,Angle,我需要确定Python中两个n维向量之间的角度。例如,输入可以是两个列表,如下所示:[1,2,3,4]和[6,7,8,9]使用(强烈推荐),您可以执行以下操作: 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

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

使用(强烈推荐),您可以执行以下操作:

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

注意:当向量的方向相同或相反时,此操作将失败。正确的实现方法如下:

注意:如果两个向量具有相同的方向(例如,
(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.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)
来自numpy.linalg导入规范
从numpy导入点
输入数学
(a,b)之间的def角度_:
arccosInput=点(a,b)/范数(a)/范数(b)
如果arccosInput>1.0,则arccosInput=1.0,否则arccosInput
如果arccosInput<-1.0,则arccosInput=-1.0,否则arccosInput
返回math.acos(arccosInput)

请注意,如果其中一个向量的大小为零(除以0),则此函数将引发异常。

另一种可能性是仅使用
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

如果使用三维矢量,可以使用工具带简洁地执行此操作。这是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)
或通过投影计算符号角度:

vg.angle(vec1, vec2, look=vg.basis.z)
vg.signed_angle(vec1, vec2, look=vg.basis.z)
我在上一次创业时创建了这个库,它的动机是这样的:简单的想法在NumPy中是冗长或不透明的。

对于少数人来说(由于搜索引擎优化的复杂性),他们在这里试图计算python中两行之间的角度,如
(x0,y0),(x1,y1)
几何行,有以下最低解决方案(使用
shapely
模块,但可以轻松修改为不使用):

而用途将是

>>> 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
很好,但是

如果你想拥有符号角度,你必须确定给定的一对是右手还是左手(有关更多信息,请参阅)

我的解决方案是:

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))
它并不完美,因为这是一个
未实现的错误
,但就我而言,它运行良好。这种行为是可以修复的(因为任何给定的一对都需要确定操作性),但我需要编写更多的代码。

建立在佩珀军士长的基础上,增加对对齐向量的支持,并使用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±820ns(7次运行的平均值±标准偏差,每个10000个回路)

找到两个向量之间角度的简单方法(适用于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

使用numpy中的一些函数

import numpy as np

def dot_product_angle(v1,v2):

    if np.linalg.norm(v1) == 0 or np.linalg.norm(v2) == 0:
        print("Zero magnitude vector!")
    else:
        vector_dot_product = np.dot(v1,v2)
        arccos = np.arccos(vector_dot_product / (np.linalg.norm(v1) * np.linalg.norm(v2)))
        angle = np.degrees(arccos)
        return angle
    return 0

另外,如果您只需要角的cos、sin、tan,而不需要角本身,那么您可以跳过math.acos来获得余弦,并使用叉积来获得正弦。假设
math.sqrt(x)
相当于
x**0.5
math.pow(x,y)
相当于
x**y
,我很惊讶,在Python2.x->3.0的转换过程中,这些冗余斧头居然幸存了下来。在实践中,我通常做这些数字的事情,作为更大的计算密集型过程的一部分,解释器支持“**”直接进入字节码二进制函数,而不是查找“math”,访问其属性“sqrt”,然后是速度非常慢的字节码调用函数,可以在无需编码或可读性成本的情况下显著提高速度。如numpy的答案所示:如果舍入误差起作用,这可能会失败!这可能发生在并行和反并行向量上!注意:如果向量相同(例如,
角度((1,1,1.),(1,1,1.))
),则此操作将失败。请参阅我的答案,以获得更正确的版本。如果您在讨论上面的实现,那么它失败是因为舍入错误,而不是因为向量是平行的。最后一行可能会导致错误,正如我发现的那样,因为舍入错误。因此,如果你点(u,u)/norm(u)**2,结果是1.0000000002,arccos然后失败(对于反平行向量也“有效”),我已经用u=[1,1,1]进行了测试。u=[1,1,1,1]工作正常,但添加的每个维度返回的值都略大于或大于1…注意:当两个向量的方向相同或相反时,此操作将失败(yield
nan
)。有关更正确的版本,请参见我的答案。添加neo的评论,最后一行应该是
angle=arccos(clip(c,-1,1))
,以避免舍入问题。这就解决了@DavidWolever的问题。对于使用上面代码片段的人:
clip
应该添加到numpy导入列表中。使用
np.isnan
而不是数学库中的那一个不是更好吗?理论上它们应该是相同的,但实际上我不太确定。无论哪种方式,我都认为这样会更安全。我的numpy(版本==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 Out[141]:0.0省略了至少一个输入向量为零向量的特殊情况,这对于单位向量的除法是有问题的。在这种情况下,一种可能是只返回此函数中的输入向量。((0,0,0),(0,1,0))之间的角度_将给出nan作为结果,而不是90@kafman0矢量的角度未定义(在数学中)。这是最好的答案,因为它正是数学表达式θ=atan2(u^v,u.v)。这是永远不会失败的!这是给2个人的