Question

I besoin pour déterminer l'angle (s) entre deux vecteurs n-dimensionnels dans python. Par exemple, l'entrée peut être deux listes comme suit:. [1,2,3,4] et [6,7,8,9]

Était-ce utile?

La solution

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

Remarque : cela ne fonctionne pas lorsque les vecteurs ont les mêmes ou la direction opposée. La mise en œuvre correcte est ici: https://stackoverflow.com/a/13849249/71522

Autres conseils

Remarque :. Toutes les autres réponses ici échouera si les deux vecteurs ont soit la même direction (ex, (1, 0, 0), (1, 0, 0)) ou des directions opposées (ex, (-1, 0, 0), (1, 0, 0))

Voici une fonction qui gérer correctement ces cas:

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

vous faire à l'aide numpy (fortement recommandé),:

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

L'autre possibilité est juste en utilisant numpy et il vous donne l'angle intérieur

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)

et est ici la sortie:

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

Si vous travaillez avec des vecteurs 3D, vous pouvez le faire en utilisant de façon concise le toolbelt vg . Il est une légère couche au-dessus de numpy.

import numpy as np
import vg

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

vg.angle(vec1, vec2)

Vous pouvez également spécifier un angle de vue pour calculer l'angle par projection:

vg.angle(vec1, vec2, look=vg.basis.z)

Ou calculer l'angle signé par projection:

vg.signed_angle(vec1, vec2, look=vg.basis.z)

J'ai créé la bibliothèque de mon dernier démarrage, où il a été motivé par des utilisations comme ceci:. Idées simples qui sont verbeux ou opaques dans NumPy

Pour les rares qui peuvent avoir (en raison de complications SEO) a pris fin en essayant de calculer l'angle entre deux lignes en python, comme dans (x0, y0), (x1, y1) lignes géométriques, il est le dessous minimum solution (utilise le module shapely, mais peut être facilement modifié pas):

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)

Et l'utilisation serait

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

Utilisation numpy et en prenant soin des erreurs d'arrondi de bande interdite:

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, cette fonction lancera une exception si l'un des vecteurs a une magnitude zéro (division par 0).

David solution de Wolever est bon , mais

Si vous voulez avoir signé angles vous devez déterminer si une paire donnée est droitier ou gaucher (voir wiki pour plus d'infos).

Ma solution pour cela est:

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

Il est pas parfait à cause de cette NotImplementedError mais pour mon cas, il fonctionne bien. Ce comportement pourrait être fixé (cause handness est déterminée pour une paire donnée) mais il faut plus de code que je veux et je dois écrire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top