在R中找到矩阵的相邻元素 [英] find neighbouring elements of a matrix in R

查看:104
本文介绍了在R中找到矩阵的相邻元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

非常感谢以下用户做出的巨大贡献,并感谢Gregor进行了基准测试.

huge thanks to the users below for great contributions and to Gregor for benchmarking.

说我有一个像这样的整数值填充的矩阵...

Say I have a matrix filled with integer values like this...

    mat <- matrix(1:100, 10, 10)

我可以像这样创建每个元素的x,y坐标列表...

I can create a list of x, y coordinates of each element like this...

    addresses <- expand.grid(x = 1:10, y = 1:10)

现在,对于这些坐标中的每个坐标(即,对于垫中的每个元素),我都希望找到相邻的元素(包括对角线,它应该使8个相邻).

Now for each of these coordinates (i.e. for each element in mat) i would like to find the neighboring elements (including diagonals this should make 8 neighbors).

我敢肯定有一种简单的方法,任何人都可以帮忙吗?

I'm sure there is an easy way, can anyone help?

到目前为止,我一直尝试遍历并为每个元素记录相邻元素,如下所示;

What I have tried so far is to loop through and for each element record the neighboring elements as follows;

    neighbours <- list()
    for(i in 1:dim(addresses)[1]){
      x <- addresses$x[i]
      y <- addresses$y[i]
      neighbours[[i]] <- c(mat[y-1, x  ],
                           mat[y-1, x+1],
                           mat[y  , x+1],
                           mat[y+1, x+1],
                           mat[y+1, x  ],
                           mat[y+1, x-1],
                           mat[y  , x-1],
                           mat[y-1, x-1])
    }

当它碰到矩阵的边缘时,尤其是当索引大于矩阵的边缘时,就会遇到问题.

This runs into problems when it hits the edge of the matrix, particularly when the index is greater than the edge of the matrix.

推荐答案

这是一个很好的示例.我做了4x4,所以我们可以很容易地看到它,但是都可以通过n进行调整.它也已完全矢量化,因此应该具有良好的速度.

Here's a nice example. I did 4x4 so we can see it easily, but it's all adjustable by n. It's also fully vectorized so should have good speed.

n = 4
mat = matrix(1:n^2, nrow = n)
mat.pad = rbind(NA, cbind(NA, mat, NA), NA)

在填充矩阵的情况下,相邻元素只是n×n个子矩阵而四处移动.使用指南针方向作为标签:

With the padded matrix, the neighbors are just n by n submatrices, shifting around. Using compass directions as labels:

ind = 2:(n + 1) # row/column indices of the "middle"
neigh = rbind(N  = as.vector(mat.pad[ind - 1, ind    ]),
              NE = as.vector(mat.pad[ind - 1, ind + 1]),
              E  = as.vector(mat.pad[ind    , ind + 1]),
              SE = as.vector(mat.pad[ind + 1, ind + 1]),
              S  = as.vector(mat.pad[ind + 1, ind    ]),
              SW = as.vector(mat.pad[ind + 1, ind - 1]),
              W  = as.vector(mat.pad[ind    , ind - 1]),
              NW = as.vector(mat.pad[ind - 1, ind - 1]))

mat
#      [,1] [,2] [,3] [,4]
# [1,]    1    5    9   13
# [2,]    2    6   10   14
# [3,]    3    7   11   15
# [4,]    4    8   12   16

  neigh[, 1:6]
#    [,1] [,2] [,3] [,4] [,5] [,6]
# N    NA    1    2    3   NA    5
# NE   NA    5    6    7   NA    9
# E     5    6    7    8    9   10
# SE    6    7    8   NA   10   11
# S     2    3    4   NA    6    7
# SW   NA   NA   NA   NA    2    3
# W    NA   NA   NA   NA    1    2
# NW   NA   NA   NA   NA   NA    1

因此您可以看到第一个元素mat[1,1],它从North开始并按顺时针方向旋转,相邻元素是neigh的第一列.下一个元素是mat[2,1],依此类推,直到mat的列. (您也可以将其与@mrip的答案进行比较,以了解我们的列具有相同的元素,只是顺序不同.)

So you can see for the first element mat[1,1], starting at North and going clockwise the neighbors are the first column of neigh. The next element is mat[2,1], and so on down the columns of mat. (You can also compare to @mrip's answer and see that our columns have the same elements, just in a different order.)

小矩阵

mat = matrix(1:16, nrow = 4)
mbm(gregor(mat), mrip(mat), marat(mat), u20650(mat), times = 100)
# Unit: microseconds
#         expr     min       lq      mean   median       uq      max neval  cld
#  gregor(mat)  25.054  30.0345  34.04585  31.9960  34.7130   61.879   100 a   
#    mrip(mat) 420.167 443.7120 482.44136 466.1995 483.4045 1820.121   100   c 
#   marat(mat) 746.462 784.0410 812.10347 808.1880 832.4870  911.570   100    d
#  u20650(mat) 186.843 206.4620 220.07242 217.3285 230.7605  269.850   100  b  

在更大的矩阵上,我不得不使用user20650的功能,因为它试图分配232.8 Gb向量,并且在等待大约10分钟后,我也拿出了Marat的答案.

On a larger matrix I had to take out user20650's function because it tried to allocate a 232.8 Gb vector, and I also took out Marat's answer after waiting for about 10 minutes.

mat = matrix(1:500^2, nrow = 500)

mbm(gregor(mat), mrip(mat), times = 100)
# Unit: milliseconds
#         expr       min        lq      mean    median        uq      max neval cld
#  gregor(mat) 19.583951 21.127883 30.674130 21.656866 22.433661 127.2279   100   b
#    mrip(mat)  2.213725  2.368421  8.957648  2.758102  2.958677 104.9983   100  a 

因此,在任何时间都很重要的情况下,@ mrip的解决方案是迄今为止最快的.

So it looks like in any case where time matters, @mrip's solutions is by far the fastest.

使用的功能:

gregor = function(mat) {
    n = nrow(mat)
    mat.pad = rbind(NA, cbind(NA, mat, NA), NA)
    ind = 2:(n + 1) # row/column indices of the "middle"
    neigh = rbind(N  = as.vector(mat.pad[ind - 1, ind    ]),
                  NE = as.vector(mat.pad[ind - 1, ind + 1]),
                  E  = as.vector(mat.pad[ind    , ind + 1]),
                  SE = as.vector(mat.pad[ind + 1, ind + 1]),
                  S  = as.vector(mat.pad[ind + 1, ind    ]),
                  SW = as.vector(mat.pad[ind + 1, ind - 1]),
                  W  = as.vector(mat.pad[ind    , ind - 1]),
                  NW = as.vector(mat.pad[ind - 1, ind - 1]))
    return(neigh)
}

mrip = function(mat) {
    m2<-cbind(NA,rbind(NA,mat,NA),NA)
    addresses <- expand.grid(x = 1:4, y = 1:4)
    ret <- c()
    for(i in 1:-1)
        for(j in 1:-1)
            if(i!=0 || j !=0)
                ret <- rbind(ret,m2[addresses$x+i+1+nrow(m2)*(addresses$y+j)]) 
    return(ret)
}

get.neighbors <- function(rw, z, mat) {
    # Convert to absolute addresses 
    z2 <- t(z + unlist(rw))
    # Choose those with indices within mat 
    b.good <- rowSums(z2 > 0)==2  &  z2[,1] <= nrow(mat)  &  z2[,2] <= ncol(mat)
    mat[z2[b.good,]]
}

marat = function(mat) {
    n.row = n.col = nrow(mat)
    addresses <- expand.grid(x = 1:n.row, y = 1:n.col)
    # Relative addresses
    z <- rbind(c(-1,0,1,-1,1,-1,0,1), c(-1,-1,-1,0,0,1,1,1))
    apply(addresses, 1,
          get.neighbors, z = z, mat = mat) # Returns a list with neighbors
}

u20650 = function(mat) {
    w <-  which(mat==mat, arr.ind=TRUE)
    d <- as.matrix(dist(w, "maximum", diag=TRUE, upper=TRUE))
    # extract neighbouring values for each element
    # extract where max distance is one
    a <- apply(d, 1, function(i) mat[i == 1] )
    names(a)  <- mat
    return(a)
}

这篇关于在R中找到矩阵的相邻元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆