R: split matrix into arbitrary number of blocks

房东的猫 提交于 2019-12-23 23:48:28

问题


Say I have a matrix of values

    set.seed(1)
    A <- matrix(runif(25),ncol=5)

I'd like to calculate some statistics for approximately square neighborhoods within this matrix of approximately equal size. Either of these kinds of output would do:

    N1 <-  matrix(c(rep(c("A","A","B","B","B"),2),rep(c("C","C","D","D","D"),3)),ncol=5)
    N2 <-  matrix(c(rep(c("A","A","A","B","B"),3),rep(c("C","C","D","D","D"),2)),ncol=5)
    N1
         [,1] [,2] [,3] [,4] [,5]
    [1,] "A"  "A"  "C"  "C"  "C" 
    [2,] "A"  "A"  "C"  "C"  "C" 
    [3,] "B"  "B"  "D"  "D"  "D" 
    [4,] "B"  "B"  "D"  "D"  "D" 
    [5,] "B"  "B"  "D"  "D"  "D" 

    N2
         [,1] [,2] [,3] [,4] [,5]
    [1,] "A"  "A"  "A"  "C"  "C" 
    [2,] "A"  "A"  "A"  "C"  "C" 
    [3,] "A"  "A"  "A"  "D"  "D" 
    [4,] "B"  "B"  "B"  "D"  "D" 
    [5,] "B"  "B"  "B"  "D"  "D" 

other approximations are also OK, since I can always rotate the matrix. Then I can use these neighborhood matrices to calculate stats using tapply(), like this:

    tapply(A,N1,mean)
            A         B         C         D 
    0.6201744 0.5057402 0.4574495 0.5594227

What I want is a function that can make me a matrix of arbitrary dimensions with an arbitrary number of block-like neighborhoods like N1 or N2. I'm having a hard time trying to figure out how such a function would deal with situations where the desired number of blocks are not even squares. N1 and N2 have 4 neighborhoods, but say I wanted 5 for some output something like this:

    N3 <-  matrix(c("A","A","B","B","B","A","A","C","C","C","D","D","C","C","C",
            "D","D","E","E","E","D","D","E","E","E"),ncol=5)
         [,1] [,2] [,3] [,4] [,5]
    [1,] "A"  "A"  "D"  "D"  "D" 
    [2,] "A"  "A"  "D"  "D"  "D" 
    [3,] "B"  "C"  "C"  "E"  "E" 
    [4,] "B"  "C"  "C"  "E"  "E" 
    [5,] "B"  "C"  "C"  "E"  "E" 

Does anyone know of an existing function that can do this kind of split, or have any ideas on how to make one? Thank you!

[[Edit]] My final function, taking into account Vincent's advice:

    DecideBLocks <- function(A,nhoods){
        nc <- ncol(A)
        nr <- nrow(A)
        nhood_side <- floor(sqrt((nc*nr)/nhoods))
        Neighborhoods <- matrix(paste(ceiling(col(A)/nhood_side), ceiling(row(A)/nhood_side), sep="-"), nc=ncol(A)) 
        nhoods.out <- length(unique(c(Neighborhoods)))
        if (nhoods.out != nhoods){
            cat(nhoods.out,"neighborhoods created.\nThese were on average",nhood_side,"by",nhood_side,"cells\nit's a different number than that stated the function tries to round things to square neighborhoods\n")
        }
        return(Neighborhoods)
    }
    A <- matrix(rnorm(120),12)
    B <- DecideBLocks(A,13)

回答1:


You can try to play with the row and col functions: they reduce the problem to a 1-dimensional one. The following defines blocks of size at most 2*2.

matrix( 
  paste(
    ceiling(col(A)/2), 
    ceiling(row(A)/2), 
    sep="-"), 
  nc=ncol(A) 
)



回答2:


You can choose your bdeep (row-spec) and bwide (co-spec) parameters near the center of youree matrix dimensions in whatever manner you like and use this simple function to construct your matrix. As long as the bwide and bdeep are equal, and nrow==ncol, you should get square sub-matrices.

 mkblk <- function(bwide, bdeep, nrow, ncol){
   bstr1 <- c(rep("A", bdeep), rep("B", nrow-bdeep))
   bstr2 <- c(rep("C", bdeep), rep("D", nrow-bdeep))
   matrix(c( rep(bstr1, bwide), rep(bstr2, ncol-bwide)), ncol=ncol, nrow=nrow)}
 mkblk(2,2,5,5)

     [,1] [,2] [,3] [,4] [,5]
[1,] "A"  "A"  "C"  "C"  "C" 
[2,] "A"  "A"  "C"  "C"  "C" 
[3,] "B"  "B"  "D"  "D"  "D" 
[4,] "B"  "B"  "D"  "D"  "D" 
[5,] "B"  "B"  "D"  "D"  "D" 

#Test of your strategy
 tapply(A, mkblk(2,2,5,5), mean)
        A         B         C         D 
0.6201744 0.5057402 0.4574495 0.5594227 


来源:https://stackoverflow.com/questions/9430950/r-split-matrix-into-arbitrary-number-of-blocks

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