Angles Between Two N-Dimensional Vectors in Python

Angles between two n-dimensional vectors in Python

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

Note: this will fail when the vectors have either the same or the opposite direction. The correct implementation is here: https://stackoverflow.com/a/13849249/71522

Fastest way to compute angle between 2D vectors

Use scipy.spatial.distance.pdist:

import numpy as np
import scipy.spatial.distance

vectors = np.array([[1, 3], [2, 4], [3, 5]])
# Compute cosine distance
dist = scipy.spatial.distance.pdist(vectors, 'cosine')
# Compute angles
angle = np.rad2deg(np.arccos(1 - dist))
# Make it into a matrix
angle_matrix = scipy.spatial.distance.squareform(angle)
print(angle_matrix)
# [[ 0. 8.13010235 12.52880771]
# [ 8.13010235 0. 4.39870535]
# [12.52880771 4.39870535 0. ]]

Calculate the angle between the rows of two matrices in numpy

We could use einsum to replace the dot-product computations and axis param for the norm ones to have a vectorized solution, like so -

def angle_rowwise(A, B):
p1 = np.einsum('ij,ij->i',A,B)
p2 = np.linalg.norm(A,axis=1)
p3 = np.linalg.norm(B,axis=1)
p4 = p1 / (p2*p3)
return np.arccos(np.clip(p4,-1.0,1.0))

We could optimize further and bring in more of einsum, specifically to compute norms with it. Hence, we could use it like so -

def angle_rowwise_v2(A, B):
p1 = np.einsum('ij,ij->i',A,B)
p2 = np.einsum('ij,ij->i',A,A)
p3 = np.einsum('ij,ij->i',B,B)
p4 = p1 / np.sqrt(p2*p3)
return np.arccos(np.clip(p4,-1.0,1.0))

Hence, to solve our case to get output in degrees -

out = angle_rowwise(A, B)*180/np.pi

Generating two vectors with a given angle between them

For a given starting vector u and cosine similarity c:

  • Generate 2 points in n-D space; call them a and b
  • Project b onto the plane orthogonal to u and containing a
  • Subtract a from the result to obtain a vector orthogonal to u; call it h
  • Use [u,h] as a basis and basic trigonometry to generate the desired vector v

The above method is dimension-agnostic as it only uses dot products. The resultant vectors {v} are of unit length and uniformly distributed around u.

Sample Image

Sample Image

Define vectors and find angles using Python in 3D space?

I think your problem might be how you're defining your vectors. If I do everything exactly like you describe in your question, then I also get a sequence of fluctuating angles:

import pandas as pd
import numpy as np

def ang(u, v):
# see https://stackoverflow.com/a/2827466/425458
c = np.dot(u/np.linalg.norm(u), v/np.linalg.norm(v))
return np.rad2deg(np.arccos(np.clip(c, -1, 1)))

d = ''' x y z
0 0.722950 0.611143 0.154976
1 0.722887 0.611518 0.152955
2 0.722880 0.612001 0.150593
3 0.722910 0.612509 0.148238
4 0.723049 0.613053 0.146069
5 0.723113 0.613583 0.143714
6 0.722763 0.613838 0.141321
7 0.721956 0.613876 0.138467
8 0.721638 0.614167 0.136008
9 0.720665 0.614093 0.133143
10 0.719612 0.613956 0.130317
11 0.718672 0.613882 0.127562
12 0.717771 0.613870 0.124638
13 0.716533 0.613668 0.121512'''

df = pd.read_csv(pd.compat.StringIO(d), sep='\s+')
xyz = df.values
u = np.diff(xyz, axis=0)

head = np.array([0.5806, 0.50239, 0.54057])
tail = np.array([0.5806, 0.50239, 0. ])
v = head - tail

ang(u, v)
# output:
# array([101.96059029, 104.01677172, 103.97438663, 102.85092705,
# 103.97438663, 104.20457158, 107.01708978, 104.604926 ,
# 107.08468905, 106.84512875, 106.40978005, 107.44768844,
# 108.69610224])

However, if you treat your list of xyz points as vectors (ie the vectors starting at the origin and going to each of the points), then you do see a constantly increasing angle between the reference vector and the sequence of vectors, like you expected:

ang(xyz, v)
# output:
# array([87.51931013, 87.55167997, 87.58951053, 87.62722792, 87.66196546,
# 87.69968089, 87.73800388, 87.78370828, 87.82308596, 87.8689639 ,
# 87.91421599, 87.95832992, 88.0051486 , 88.05520021])

Might this be the actually correct way to interpret/analyze your data?



Related Topics



Leave a reply



Submit