Angle between vector and list of vectors in R

ぃ、小莉子 提交于 2021-02-07 14:42:32

问题


When comparing two vectors it is simple to calculate the angle between them, but in R it is noticeably harder to calculate the angle between a vector and a matrix of vectors efficiently.

Say you have a 2D vector A=(2, 0) and then a matrix B={(1,3), (-2,4), (-3,-3), (1,-4)}. I am interested in working out the smallest angle between A and the vectors in B. If I try to use

min(acos( sum(a%*%b) / ( sqrt(sum(a %*% a)) * sqrt(sum(b %*% b)) ) ))

it fails as they are non-conformable arguments.

Is there any code similar to that of above which can handle a vector and matrix?

Note: At the risk of being marked as a duplicate the solutions found in several sources do not apply in this case

Edit: The reason for this is I have a large matrix X, and A is just one row of this. I am reducing the number of elements based solely on the angle of each vector. The first element of B is the first in X, and then if the angle between any element in B and the next element X[,2] (here A) is greater than a certain tolerance, this is added to the list B. I am just using B<-rbind(B,X[,2]) to do this, so this results in B being a matrix.


回答1:


You don't describe the format of A and B in detail, so I assume they are matrices by rows.

(A <- c(2, 0))
# [1] 2 0

(B <- rbind(c(1,3), c(-2,4), c(-3,-3), c(1,-4)))
#      [,1] [,2]
# [1,]    1    3
# [2,]   -2    4
# [3,]   -3   -3
# [4,]    1   -4

Solution 1 with apply():

apply(B, 1, FUN = function(x){
  acos(sum(x*A) / (sqrt(sum(x*x)) * sqrt(sum(A*A))))
})

# [1] 1.249046 2.034444 2.356194 1.325818

Solution 2 with sweep(): (replace sum() above with rowSums())

sweep(B, 2, A, FUN = function(x, y){
  acos(rowSums(x*y) / (sqrt(rowSums(x*x)) * sqrt(rowSums(y*y))))
})

# [1] 1.249046 2.034444 2.356194 1.325818

Solution 3 with split() and mapply:

mapply(function(x, y){
  acos(sum(x*y) / (sqrt(sum(x*x)) * sqrt(sum(y*y))))
}, split(B, row(B)), list(A))

#        1        2        3        4 
# 1.249046 2.034444 2.356194 1.325818 



回答2:


The vector of dot products between the rows of B and the vector A is B %*% A. The vector lengths of the rows of B are sqrt(rowSums(B^2)).

To find the smallest angle, you want the largest cosine, but you don't actually need to compute the angle, so the length of A doesn't matter.

Thus the row with the smallest angle will be given by row <- which.max((B %*% A)/sqrt(rowSums(B^2))). With Darren's data, that's row 1.

If you really do need the smallest angle, then you can apply the formula for two vectors to B[row,] and A. If you need all of the angles, then the formula would be

acos((B %*% A)/sqrt(rowSums(B^2))/sqrt(sum(A^2)))


来源:https://stackoverflow.com/questions/54146330/angle-between-vector-and-list-of-vectors-in-r

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!