R: splitting a matrix into an arbitrary number of blocks
Let's say I have a matrix of values
set.seed(1)
A <- matrix(runif(25),ncol=5)
I would like to calculate some statistics for approximately square neighborhoods within this matrix of approximately equal size. Any of these kinds of inference will 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 compute statistics with tapply()
, for example:
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 am struggling to figure out how such a function would deal with situations where the desired number of blocks is not even squares. N1
and N2
have 4 neighborhoods, but they say that I wanted 5 for some to 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 such a split, or have any ideas on how to create it? Thank!
[[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 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)
source to share
You can select the bdeep (row-spec) and bwide (co-spec) parameters near the center of the youree matrix dimensions in any way you want and use this simple function to create your matrix. As long as bwide and bdeep are equal and nrow == ncol, you should get square submatrices.
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
source to share