Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculate the angle between the rows of two matrices in numpy

I have two matrices consisting of 3d vectors (numpy 1D arrays) and I need to calculate the angle between the vectors, row-wise, and return the results in a 1d array. I know how to calculate the angle between two 1d vectors. What is the the proper way to do this?

*** The resulting angles are in degrees not radians.

By now I have this:

import numpy as np

A = np.array([[1,0,0],
              [0,1,0],
              [0,0,1]])

B = np.array([[1,0,1],
              [1,1,0],
              [0,1,0]])

def angle(V1,V2):
    """
    angle between vectors V1 and V2 in degrees using
    angle = arccos ( V1 dot V2 / norm(V1) * norm(V2) ) *180/np.pi
    """

    cos_of_angle = V1.dot(V2) / (np.linalg.norm(V1) * np.linalg.norm(V2)) 
    return np.arccos(np.clip(cos_of_angle,-1,1))  * 180/np.pi

Note the scaling term 180/np.pi for the conversion from rad to deg.

I would like to have an array:

C = [ angle(A[0],B[0]) , angle(A[1],B[1])...... and so on]

Really appreciated if someone could help.

like image 517
RRS Avatar asked Mar 07 '23 01:03

RRS


1 Answers

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
like image 196
Divakar Avatar answered Mar 14 '23 19:03

Divakar