Angle between two vectors in R

43,054

Solution 1

if you install/upload the library(matlib): there is a function called angle(x, y, degree = TRUE) where x and y are vectors. Note: if you have x and y in matrix form, use as.vector(x) and as.vector(y):

library(matlib)
matA <- matrix(c(3, 1), nrow = 2)  ##column vectors
matB <- matrix(c(5, 5), nrow = 2)
angle(as.vector(matA), as.vector(matB))  
##default in degrees, use degree = FALSE for radians

Solution 2

According to page 5 of this PDF, sum(a*b) is the R command to find the dot product of vectors a and b, and sqrt(sum(a * a)) is the R command to find the norm of vector a, and acos(x) is the R command for the arc-cosine. It follows that the R code to calculate the angle between the two vectors is

theta <- acos( sum(a*b) / ( sqrt(sum(a * a)) * sqrt(sum(b * b)) ) )

Solution 3

My answer consists of two parts. Part 1 is the math - to give clarity to all readers of the thread and to make the R code that follows understandable. Part 2 is the R programming.

Part 1 - Math

The dot product of two vectors x and y can be defined as:

enter image description here

where ||x|| is the Euclidean norm (also known as the L2 norm) of the vector x.

Manipulating the definition of the dot product, we can obtain:

enter image description here

where theta is the angle between the vectors x and y expressed in radians. Note that theta can take on a value that lies on the closed interval from 0 to pi.

Solving for theta itself, we get:

enter image description here

Part 2 - R Code

To translate the mathematics into R code, we need to know how to perform two matrix (vector) calculations; dot product and Euclidean norm (which is a specific type of norm, known as the L2 norm). We also need to know the R equivalent of the inverse cosine function, cos-1.

Starting from the top. By reference to ?"%*%", the dot product (also referred to as the inner product) can be calculated using the %*% operator. With reference to ?norm, the norm() function (base package) returns a norm of a vector. The norm of interest here is the L2 norm or, in the parlance of the R help documentation, the "spectral" or "2"-norm. This means that the type argument of the norm() function ought to be set equal to "2". Lastly, the inverse cosine function in R is represented by the acos() function.

Solution

Equipped with both the mathematics and the relevant R functions, a prototype function (that is, not production standard) can be put together - using Base package functions - as shown below. If the above information makes sense then the angle() function that follows should be clear without further comment.

angle <- function(x,y){
  dot.prod <- x%*%y 
  norm.x <- norm(x,type="2")
  norm.y <- norm(y,type="2")
  theta <- acos(dot.prod / (norm.x * norm.y))
  as.numeric(theta)
}

Test the function

A test to verify that the function works. Let x = (2,1) and y = (1,2). Dot product between x and y is 4. Euclidean norm of x is sqrt(5). Euclidean norm of y is also sqrt(5). cos theta = 4/5. Theta is approximately 0.643 radians.

x <- as.matrix(c(2,1))
y <- as.matrix(c(1,2))
angle(t(x),y)          # Use of transpose to make vectors (matrices) conformable.
[1] 0.6435011

I hope this helps!

Solution 4

For 2D-vectors, the way given in the accepted answer and other ones does not take into account the orientation (the sign) of the angle (angle(M,N) is the same as angle(N,M)) and it returns a correct value only for an angle between 0 and pi.

Use the atan2 function to get an oriented angle and a correct value (modulo 2pi).

angle <- function(M,N){
  acos( sum(M*N) / ( sqrt(sum(M*M)) * sqrt(sum(N*N)) ) )
}
angle2 <- function(M,N){
  atan2(N[2],N[1]) - atan2(M[2],M[1]) 
}

Check that angle2 gives the correct value:

> theta <- seq(-2*pi, 2*pi, length.out=10)
> O <- c(1,0)
> test1 <- sapply(theta, function(theta) angle(M=O, N=c(cos(theta),sin(theta))))
> all.equal(test1 %% (2*pi), theta %% (2*pi))
[1] "Mean relative difference: 1"
> test2 <- sapply(theta, function(theta) angle2(M=O, N=c(cos(theta),sin(theta))))
> all.equal(test2 %% (2*pi), theta %% (2*pi))
[1] TRUE

Solution 5

You should use the dot product. Say you have V₁ = (x₁, y₁, z₁) and V₂ = (x₂, y₂, z₂), then the dot product, which I'll denote by V₁·V₂, is calculated as

V₁·V₂ = x₁·x₂ + y₁·y₂ + z₁·z₂ = |V₁| · |V₂| · cos(θ);

What this means is that that sum shown on the left is equal to the product of the absolute values of the vectors times the cosine of the angle between the vectors. the absolute value of the vectors V₁ and V₂ are calculated as

|V₁| = √(x₁² + y₁² + z₁²), and
|V₂| = √(x₂² + y₂² + z₂²),

So, if you rearrange the first equation above, you get

cos(θ) = (x₁·x₂ + y₁·y₂ + z₁·z₂) ÷ (|V₁|·|V₂|),

and you just need the arccos function (or inverse cosine) applied to cos(θ) to get the angle.

Depending on your arccos function, the angle may be in degrees or radians.

(For two dimensional vectors, just forget the z-coordinates and do the same calculations.)

Good luck,

John Doner

Share:
43,054
Christian
Author by

Christian

I'm an Android programmer who studied bioinformatics at the Free University of Berlin. I wrote the proposal for https://biology.stackexchange.com/ and my current pet project is BetterEar. Small children can hear the difference between all kind of different phonemes but once they grow older they lose the ability to hear phoneme differences in foreign languages that their native language doesn't make. As a result most German native speakers can't hear the difference between belief and believe, between advice and advise or between effect and affect. My Android App trains the ability to hear the difference between different phonemes by giving the learner deliberate feedback. This allows the learner to regain the ability to distinguish phonemes that they lost as a child. While some people think that the way to improve information flow is through building brain-computer interfaces that can magically push information into brains, I think it makes sense to do our best to optimize the information flow of our existing channels. After school I went to study bioinformatics because I thought the way to improve the human brain is through looking at neurons. Today, I think improving the human brain is more about finding the right kind of stimulus. The human brain is an amazing learning machine on it's own and we don't need to put any electrodes into it, to train it to do better.

Updated on February 03, 2022

Comments

  • Christian
    Christian over 2 years

    What the most efficient way in the programming language R to calculate the angle between two vectors?