简体   繁体   中英

How to use some apply function to solve what requires two for-loops in R

I have a matrix, named "mat", and a smaller matrix, named "center".

temp = c(1.8421,5.6586,6.3526,2.904,3.232,4.6076,4.8,3.2909,4.6122,4.9399)
mat = matrix(temp, ncol=2)
       [,1]   [,2]
[1,] 1.8421 4.6076
[2,] 5.6586 4.8000
[3,] 6.3526 3.2909
[4,] 2.9040 4.6122
[5,] 3.2320 4.9399

center = matrix(c(3, 6, 3, 2), ncol=2)

     [,1] [,2]
[1,]    3    3
[2,]    6    2

I need to compute the distance between each row of mat with every row of center. For example, the distance of mat[1,] and center[1,] can be computed as

diff = mat[1,]-center[1,]
t(diff)%*%diff
        [,1]
[1,] 3.92511

Similarly, I can find the distance of mat[1,] and center[2,]

diff = mat[1,]-center[2,]
t(diff)%*%diff
         [,1]
[1,] 24.08771

Repeat this process for each row of mat, I will end up with

          [,1]      [,2]
[1,]  3.925110 24.087710
[2,] 10.308154  7.956554
[3,] 11.324550  1.790750
[4,]  2.608405 16.408805
[5,]  3.817036 16.304836

I know how to implement it with for-loops. I was really hoping someone could tell me how to do it with some kind of an apply() function, maybe mapply() I guess.

Thanks

apply(center, 1, function(x) colSums((x - t(mat)) ^ 2))
#           [,1]      [,2]
# [1,]  3.925110 24.087710
# [2,] 10.308154  7.956554
# [3,] 11.324550  1.790750
# [4,]  2.608405 16.408805
# [5,]  3.817036 16.304836

If you want the apply for expressiveness of code that's one thing but it's still looping, just different syntax. This can be done without any loops, or with a very small one across center instead of mat . I'd just transpose first because it's wise to get into the habit of getting as much as possible out of the apply statement. (The BrodieG answer is pretty much identical in function.) These are working because R will automatically recycle the smaller vector along the matrix and do it much faster than apply or for .

tm <- t(mat)
apply(center, 1, function(m){
    colSums((tm - m)^2) })

Use dist and then extract the relevant submatrix:

ix <- 1:nrow(mat)
as.matrix( dist( rbind(mat, center) )^2 )[ix, -ix]

          6         7
# 1  3.925110 24.087710
# 2 10.308154  7.956554
# 3 11.324550  1.790750
# 4  2.608405 16.408805
# 5  3.817036 16.304836

REVISION: simplified slightly.

This will solve it

t(apply(mat,1,function(row){
  d1<-sum((row-center[1,])^2)
  d2<-sum((row-center[2,])^2)
  return(c(d1,d2))
}))

Result:

          [,1]      [,2]
[1,]  3.925110 24.087710
[2,] 10.308154  7.956554
[3,] 11.324550  1.790750
[4,]  2.608405 16.408805
[5,]  3.817036 16.304836

You could use outer as well

d <- function(i, j) sum((mat[i, ] - center[j, ])^2) 
outer(1:nrow(mat), 1:nrow(center), Vectorize(d))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM