زوايا بين اثنين من المتجهين n-dimensial في بيثون

StackOverflow https://stackoverflow.com/questions/2827393

  •  26-09-2019
  •  | 
  •  

سؤال

أحتاج إلى تحديد الزاوية (الزاوية) بين متجهين n-dimensional في بيثون. على سبيل المثال ، يمكن أن تكون الإدخال قائمتين مثل ما يلي: [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))

استخدام نومبي (موصى به للغاية) ، ستفعل:

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

إذا كنت تعمل مع ناقلات ثلاثية الأبعاد ، فيمكنك القيام بذلك بشكل إنتاجه باستخدام شريط الأدوات 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.

بالنسبة للقلة الذين قد يكون لديهم (بسبب مضاعفات كبار المسئولين الاقتصاديين) انتهى هنا محاولة حساب الزاوية بين اثنين خطوط في بيثون ، كما في (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)

لاحظ أن هذه الوظيفة ستلقي استثناءً إذا كان لدى أحد المتجهات حجم صفر (قسمة على 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))

إنه ليس مثاليًا بسبب هذا NotImplementedError ولكن بالنسبة لحالتي ، فهي تعمل بشكل جيد. يمكن إصلاح هذا السلوك (يتم تحديد يدوي لأي زوج معين) ولكن الأمر يتطلب المزيد من التعليمات البرمجية التي أريدها ويجب أن أكتب.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top