函数将矩阵分解为R中的子矩阵。

时间:2021-06-25 16:20:24

I have a matrix M with 16 rows and 12 columns and I want to split it into a array of 16 matrices, each with 4 rows and 3 columns. I can do it manually by:

我有一个矩阵M,有16行和12列,我想把它分成16个矩阵,每个矩阵有4行和3列。我可以手动操作:

M = matrix(sample(0:127,16*12,replace=TRUE), c(16,12))

ma1 = M[1:4,1:3]
ma2 = M[1:4,4:6]
ma3 = M[1:4,7:9]
ma4 = M[1:4,10:12]

ma5 = M[5:8,1:3]
ma6 = M[5:8,4:6]
.....

But how can I create a generic function matsplitter(M, r, c) which splits M into an array of matrices, each with r rows and c columns?

但是,我如何创建一个通用的功能分配器(M, r, c),它将M分割成一个矩阵数组,每个矩阵都有r行和c列?

Thanks for your help.

谢谢你的帮助。

6 个解决方案

#1


13  

If you have a 16x12 array like this

如果你有一个16x12的数组。

mb <- structure(c("a1", "a2", "a3", "a4", "e1", "e2", "e3", "e4", "i1", 
"i2", "i3", "i4", "m1", "m2", "m3", "m4", "a5", "a6", "a7", "a8", 
"e5", "e6", "e7", "e8", "i5", "i6", "i7", "i8", "m5", "m6", "m7", 
"m8", "a9", "a10", "a11", "a12", "e9", "e10", "e11", "e12", "i9", 
"i10", "i11", "i12", "m9", "m10", "m11", "m12", "b1", "b2", "b3", 
"b4", "f1", "f2", "f3", "f4", "j1", "j2", "j3", "j4", "n1", "n2", 
"n3", "n4", "b5", "b6", "b7", "b8", "f5", "f6", "f7", "f8", "j5", 
"j6", "j7", "j8", "n5", "n6", "n7", "n8", "b9", "b10", "b11", 
"b12", "f9", "f10", "f11", "f12", "j9", "j10", "j11", "j12", 
"n9", "n10", "n11", "n12", "c1", "c2", "c3", "c4", "g1", "g2", 
"g3", "g4", "k1", "k2", "k3", "k4", "o1", "o2", "o3", "o4", "c5", 
"c6", "c7", "c8", "g5", "g6", "g7", "g8", "k5", "k6", "k7", "k8", 
"o5", "o6", "o7", "o8", "c9", "c10", "c11", "c12", "g9", "g10", 
"g11", "g12", "k9", "k10", "k11", "k12", "o9", "o10", "o11", 
"o12", "d1", "d2", "d3", "d4", "h1", "h2", "h3", "h4", "l1", 
"l2", "l3", "l4", "p1", "p2", "p3", "p4", "d5", "d6", "d7", "d8", 
"h5", "h6", "h7", "h8", "l5", "l6", "l7", "l8", "p5", "p6", "p7", 
"p8", "d9", "d10", "d11", "d12", "h9", "h10", "h11", "h12", "l9", 
"l10", "l11", "l12", "p9", "p10", "p11", "p12"), .Dim = c(16L, 
12L))

You can define matsplitter as

您可以将matsplitter定义为。

matsplitter<-function(M, r, c) {
    rg <- (row(M)-1)%/%r+1
    cg <- (col(M)-1)%/%c+1
    rci <- (rg-1)*max(cg) + cg
    N <- prod(dim(M))/r/c
    cv <- unlist(lapply(1:N, function(x) M[rci==x]))
    dim(cv)<-c(r,c,N)
    cv
} 

Then

然后

matsplitter(mb,4,3)

will return (output clipped)

将返回(输出剪)

, , 1

     [,1] [,2] [,3] 
[1,] "a1" "a5" "a9" 
[2,] "a2" "a6" "a10"
[3,] "a3" "a7" "a11"
[4,] "a4" "a8" "a12"

, , 2

     [,1] [,2] [,3] 
[1,] "b1" "b5" "b9" 
[2,] "b2" "b6" "b10"
[3,] "b3" "b7" "b11"
[4,] "b4" "b8" "b12"

, , 3

     [,1] [,2] [,3] 
[1,] "c1" "c5" "c9" 
[2,] "c2" "c6" "c10"
[3,] "c3" "c7" "c11"
[4,] "c4" "c8" "c12"

...

#2


6  

Here is a function that uses Kronecker products to do the same thing. Why? Because I enjoy Kronecker products. The bonus here is that if your row and column values don't divide evenly into your input matrix then this function will pad out the smaller matrices at the right and bottom edges with NAs so you can still have an array output.

这里有一个函数使用Kronecker产品做同样的事情。为什么?因为我喜欢克罗内克的产品。这里的好处是,如果你的行和列值不均匀地分配到你的输入矩阵中,那么这个函数将会在右边和底部边缘用NAs填充较小的矩阵,这样你仍然可以有一个数组输出。

mat_split <- function(M, r, c){
  nr <- ceiling(nrow(M)/r)
  nc <- ceiling(ncol(M)/c)
  newM <- matrix(NA, nr*r, nc*c)
  newM[1:nrow(M), 1:ncol(M)] <- M

  div_k <- kronecker(matrix(seq_len(nr*nc), nr, byrow = TRUE), matrix(1, r, c))
  matlist <- split(newM, div_k)
  N <- length(matlist)
  mats <- unlist(matlist)
  dim(mats)<-c(r, c, N)
  return(mats)
}

So using the original example:

所以,使用最初的例子:

> M = matrix(sample(0:127,16*12,replace=TRUE), c(16,12))
> mat_split(M, 4, 3)
, , 1

     [,1] [,2] [,3]
[1,]  107   45  107
[2,]   49  119   32
[3,]   79  114   26
[4,]   71  104   16

, , 2

     [,1] [,2] [,3]
[1,]   79   77    4
[2,]   46   55   49
[3,]  122   15    0
[4,]   19   12   34

, , 3

     [,1] [,2] [,3]
[1,]  114   28   74
[2,]  116   28   84
[3,]   80   49   95
[4,]   41    6   82

, , 4

     [,1] [,2] [,3]
[1,]   17   17   13
[2,]  107   78   94
[3,]   22   16   14
[4,]  104   14  117
...

but if you do this:

但如果你这样做:

mat_split(M, 4, 5)

mat_split(M,4、5)

you get:

你会得到:

, , 1

     [,1] [,2] [,3] [,4] [,5]
[1,]  107   45  107   79   77
[2,]   49  119   32   46   55
[3,]   79  114   26  122   15
[4,]   71  104   16   19   12

, , 2

     [,1] [,2] [,3] [,4] [,5]
[1,]    4  114   28   74   17
[2,]   49  116   28   84  107
[3,]    0   80   49   95   22
[4,]   34   41    6   82  104

, , 3

     [,1] [,2] [,3] [,4] [,5]
[1,]   17   13   NA   NA   NA
[2,]   78   94   NA   NA   NA
[3,]   16   14   NA   NA   NA
[4,]   14  117   NA   NA   NA

, , 4

     [,1] [,2] [,3] [,4] [,5]
[1,]  112   56   20   54   68
[2,]   59   37   30  110  126
[3,]   34   22  110   13   73
[4,]  116   57   48   77   41

...

Another useful addition might be to have the option to output as a list of matrices, instead of an array, which means you wouldn't have to pad with NAs.

另一个有用的补充可能是将选项输出为矩阵列表,而不是数组,这意味着您不必使用NAs。

#3


3  

Answer using expand.grid, using a list of rows and columns to split by. Can generalise to splitting by differently sized column/row blocks.

回答使用扩展。网格,使用一个行和列的列表来分隔。可以通过不同大小的列/行块进行划分。

M = matrix(sample(0:127,16*12,replace=TRUE), c(16,12))

split_matrix = function(M, list_of_rows,list_of_cols){
  temp = expand.grid(list_of_rows,list_of_cols)
  lapply(seq(nrow(temp)), function(i) {
  M[unlist(temp[i,1]),unlist(temp[i,2]) ]
  })
}

split_matrix(M,list(1:4,5:8,9:12,13:16),list(1:3,4:6,7:9,10:12))

#4


2  

Initial data

初始数据

M = matrix(sample(0:127,16*12,replace=TRUE), c(16,12))


> M
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
 [1,]   46   46   64   54   48   78  125   38  103    43    15   125
 [2,]   75    9   10  119  108   29   13  104   51    74    83    86
 [3,]   52   22   97   12   44  115  118  111  114    56    31    36
 [4,]    1  116   70   27   61   22   36   34   16    62    20    23
 [5,]   32   61   11   46   34  120   50   71   44   105    52    81
 [6,]   88    1   60   75   68   85    0    0   66   125    52    65
 [7,]  119   32   75   14  119   57   74  107   21    32   110    39
 [8,]  103   70   18  127   32   44   14  103  118   120     0   119
 [9,]   12   99    0   48   31  126   92   78    9    11    52    21
[10,]   51   73   22   29   53   43   75  110   80    28    26    48
[11,]   64    5   81  127   25   59   50   21   46    87    66   122
[12,]   35    9   26  100    2   97   62  101    9    26    57    58
[13,]   90   16   70  118  122  120   50  125   26    34    54    55
[14,]   40   71   25   67   14   69   39   63  102     3    20   102
[15,]   51   66   92   19    7   53   33  123   50    78    83   111
[16,]   31   10   75   55  115   20   15  126   39   114   115    62

Split by columns

拆分的列

matrices_split_by_col = lapply(1:4, function(col){
  M[,((col-1)*3+1):((col-1)*3+3)]
})


> matrices_split_by_col[[1]]
      [,1] [,2] [,3]
 [1,]   46   46   64
 [2,]   75    9   10
 [3,]   52   22   97
 [4,]    1  116   70
 [5,]   32   61   11
 [6,]   88    1   60
 [7,]  119   32   75
 [8,]  103   70   18
 [9,]   12   99    0
[10,]   51   73   22
[11,]   64    5   81
[12,]   35    9   26
[13,]   90   16   70
[14,]   40   71   25
[15,]   51   66   92
[16,]   31   10   75

Now do two lapplies to split each column into rows

现在执行两个lapply将每个列分割成行。

matrices_split_by_row = lapply(matrices_split_by_col, function(mat){

  lapply(1:4, function(row){
    mat[((row-1)*3+1):((row-1)*3+4),]
  })

})

Unlist the result:

Unlist结果:

matrices_split_by_row_and_col = unlist(matrices_split_by_row,recursive=FALSE)

Check result:

检查结果:

> matrices_split_by_row_and_col[[2]]
     [,1] [,2] [,3]
[1,]    1  116   70
[2,]   32   61   11
[3,]   88    1   60
[4,]  119   32   75

Oops, this gives the matrices going down the columns first, but anyway, you can modify the code and turn it into a function if you want, using the underlying logic.

噢,这让矩阵先从列开始,但不管怎样,你可以修改代码,如果你想,可以把它转换成一个函数,使用底层逻辑。

#5


2  

Using my limited regular programming knowledge, I came up with following code:

利用有限的常规编程知识,我提出了以下代码:

matsplitter = function(mat, submatr, submatc){
    matr = dim(mat)[1]
    matc = dim(mat)[2]
    mats_per_row=matc/submatc

    submat = array(NA, c(submatr,submatc,matr*matc/(submatr*submatc)))

    cur_submat=1; k=0
    i=j=a=b=1

    while(TRUE){
        submat[i,j,cur_submat+k] = mat[a,b]

        j=j+1
        if(j>submatc){j=1; k=k+1; if(k>(mats_per_row-1)){k=0; i=i+1; if(i>submatr){i=1;cur_submat=cur_submat+mats_per_row;}}}

        b=b+1
        if(b>matc){b=1;a=a+1; if(a>matr){break};}
    }
    submat
}

#6


1  

Modification of @MrFlick's answer:

修改@MrFlick的回答是:

matsplitter<-function(M, r, c) {
  simplify2array(lapply(
    split(M, interaction((row(M)-1)%/%r+1,(col(M)-1)%/%c+1)),
    function(x) {dim(x) <- c(r,c); x;}
  ))
} 

#1


13  

If you have a 16x12 array like this

如果你有一个16x12的数组。

mb <- structure(c("a1", "a2", "a3", "a4", "e1", "e2", "e3", "e4", "i1", 
"i2", "i3", "i4", "m1", "m2", "m3", "m4", "a5", "a6", "a7", "a8", 
"e5", "e6", "e7", "e8", "i5", "i6", "i7", "i8", "m5", "m6", "m7", 
"m8", "a9", "a10", "a11", "a12", "e9", "e10", "e11", "e12", "i9", 
"i10", "i11", "i12", "m9", "m10", "m11", "m12", "b1", "b2", "b3", 
"b4", "f1", "f2", "f3", "f4", "j1", "j2", "j3", "j4", "n1", "n2", 
"n3", "n4", "b5", "b6", "b7", "b8", "f5", "f6", "f7", "f8", "j5", 
"j6", "j7", "j8", "n5", "n6", "n7", "n8", "b9", "b10", "b11", 
"b12", "f9", "f10", "f11", "f12", "j9", "j10", "j11", "j12", 
"n9", "n10", "n11", "n12", "c1", "c2", "c3", "c4", "g1", "g2", 
"g3", "g4", "k1", "k2", "k3", "k4", "o1", "o2", "o3", "o4", "c5", 
"c6", "c7", "c8", "g5", "g6", "g7", "g8", "k5", "k6", "k7", "k8", 
"o5", "o6", "o7", "o8", "c9", "c10", "c11", "c12", "g9", "g10", 
"g11", "g12", "k9", "k10", "k11", "k12", "o9", "o10", "o11", 
"o12", "d1", "d2", "d3", "d4", "h1", "h2", "h3", "h4", "l1", 
"l2", "l3", "l4", "p1", "p2", "p3", "p4", "d5", "d6", "d7", "d8", 
"h5", "h6", "h7", "h8", "l5", "l6", "l7", "l8", "p5", "p6", "p7", 
"p8", "d9", "d10", "d11", "d12", "h9", "h10", "h11", "h12", "l9", 
"l10", "l11", "l12", "p9", "p10", "p11", "p12"), .Dim = c(16L, 
12L))

You can define matsplitter as

您可以将matsplitter定义为。

matsplitter<-function(M, r, c) {
    rg <- (row(M)-1)%/%r+1
    cg <- (col(M)-1)%/%c+1
    rci <- (rg-1)*max(cg) + cg
    N <- prod(dim(M))/r/c
    cv <- unlist(lapply(1:N, function(x) M[rci==x]))
    dim(cv)<-c(r,c,N)
    cv
} 

Then

然后

matsplitter(mb,4,3)

will return (output clipped)

将返回(输出剪)

, , 1

     [,1] [,2] [,3] 
[1,] "a1" "a5" "a9" 
[2,] "a2" "a6" "a10"
[3,] "a3" "a7" "a11"
[4,] "a4" "a8" "a12"

, , 2

     [,1] [,2] [,3] 
[1,] "b1" "b5" "b9" 
[2,] "b2" "b6" "b10"
[3,] "b3" "b7" "b11"
[4,] "b4" "b8" "b12"

, , 3

     [,1] [,2] [,3] 
[1,] "c1" "c5" "c9" 
[2,] "c2" "c6" "c10"
[3,] "c3" "c7" "c11"
[4,] "c4" "c8" "c12"

...

#2


6  

Here is a function that uses Kronecker products to do the same thing. Why? Because I enjoy Kronecker products. The bonus here is that if your row and column values don't divide evenly into your input matrix then this function will pad out the smaller matrices at the right and bottom edges with NAs so you can still have an array output.

这里有一个函数使用Kronecker产品做同样的事情。为什么?因为我喜欢克罗内克的产品。这里的好处是,如果你的行和列值不均匀地分配到你的输入矩阵中,那么这个函数将会在右边和底部边缘用NAs填充较小的矩阵,这样你仍然可以有一个数组输出。

mat_split <- function(M, r, c){
  nr <- ceiling(nrow(M)/r)
  nc <- ceiling(ncol(M)/c)
  newM <- matrix(NA, nr*r, nc*c)
  newM[1:nrow(M), 1:ncol(M)] <- M

  div_k <- kronecker(matrix(seq_len(nr*nc), nr, byrow = TRUE), matrix(1, r, c))
  matlist <- split(newM, div_k)
  N <- length(matlist)
  mats <- unlist(matlist)
  dim(mats)<-c(r, c, N)
  return(mats)
}

So using the original example:

所以,使用最初的例子:

> M = matrix(sample(0:127,16*12,replace=TRUE), c(16,12))
> mat_split(M, 4, 3)
, , 1

     [,1] [,2] [,3]
[1,]  107   45  107
[2,]   49  119   32
[3,]   79  114   26
[4,]   71  104   16

, , 2

     [,1] [,2] [,3]
[1,]   79   77    4
[2,]   46   55   49
[3,]  122   15    0
[4,]   19   12   34

, , 3

     [,1] [,2] [,3]
[1,]  114   28   74
[2,]  116   28   84
[3,]   80   49   95
[4,]   41    6   82

, , 4

     [,1] [,2] [,3]
[1,]   17   17   13
[2,]  107   78   94
[3,]   22   16   14
[4,]  104   14  117
...

but if you do this:

但如果你这样做:

mat_split(M, 4, 5)

mat_split(M,4、5)

you get:

你会得到:

, , 1

     [,1] [,2] [,3] [,4] [,5]
[1,]  107   45  107   79   77
[2,]   49  119   32   46   55
[3,]   79  114   26  122   15
[4,]   71  104   16   19   12

, , 2

     [,1] [,2] [,3] [,4] [,5]
[1,]    4  114   28   74   17
[2,]   49  116   28   84  107
[3,]    0   80   49   95   22
[4,]   34   41    6   82  104

, , 3

     [,1] [,2] [,3] [,4] [,5]
[1,]   17   13   NA   NA   NA
[2,]   78   94   NA   NA   NA
[3,]   16   14   NA   NA   NA
[4,]   14  117   NA   NA   NA

, , 4

     [,1] [,2] [,3] [,4] [,5]
[1,]  112   56   20   54   68
[2,]   59   37   30  110  126
[3,]   34   22  110   13   73
[4,]  116   57   48   77   41

...

Another useful addition might be to have the option to output as a list of matrices, instead of an array, which means you wouldn't have to pad with NAs.

另一个有用的补充可能是将选项输出为矩阵列表,而不是数组,这意味着您不必使用NAs。

#3


3  

Answer using expand.grid, using a list of rows and columns to split by. Can generalise to splitting by differently sized column/row blocks.

回答使用扩展。网格,使用一个行和列的列表来分隔。可以通过不同大小的列/行块进行划分。

M = matrix(sample(0:127,16*12,replace=TRUE), c(16,12))

split_matrix = function(M, list_of_rows,list_of_cols){
  temp = expand.grid(list_of_rows,list_of_cols)
  lapply(seq(nrow(temp)), function(i) {
  M[unlist(temp[i,1]),unlist(temp[i,2]) ]
  })
}

split_matrix(M,list(1:4,5:8,9:12,13:16),list(1:3,4:6,7:9,10:12))

#4


2  

Initial data

初始数据

M = matrix(sample(0:127,16*12,replace=TRUE), c(16,12))


> M
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
 [1,]   46   46   64   54   48   78  125   38  103    43    15   125
 [2,]   75    9   10  119  108   29   13  104   51    74    83    86
 [3,]   52   22   97   12   44  115  118  111  114    56    31    36
 [4,]    1  116   70   27   61   22   36   34   16    62    20    23
 [5,]   32   61   11   46   34  120   50   71   44   105    52    81
 [6,]   88    1   60   75   68   85    0    0   66   125    52    65
 [7,]  119   32   75   14  119   57   74  107   21    32   110    39
 [8,]  103   70   18  127   32   44   14  103  118   120     0   119
 [9,]   12   99    0   48   31  126   92   78    9    11    52    21
[10,]   51   73   22   29   53   43   75  110   80    28    26    48
[11,]   64    5   81  127   25   59   50   21   46    87    66   122
[12,]   35    9   26  100    2   97   62  101    9    26    57    58
[13,]   90   16   70  118  122  120   50  125   26    34    54    55
[14,]   40   71   25   67   14   69   39   63  102     3    20   102
[15,]   51   66   92   19    7   53   33  123   50    78    83   111
[16,]   31   10   75   55  115   20   15  126   39   114   115    62

Split by columns

拆分的列

matrices_split_by_col = lapply(1:4, function(col){
  M[,((col-1)*3+1):((col-1)*3+3)]
})


> matrices_split_by_col[[1]]
      [,1] [,2] [,3]
 [1,]   46   46   64
 [2,]   75    9   10
 [3,]   52   22   97
 [4,]    1  116   70
 [5,]   32   61   11
 [6,]   88    1   60
 [7,]  119   32   75
 [8,]  103   70   18
 [9,]   12   99    0
[10,]   51   73   22
[11,]   64    5   81
[12,]   35    9   26
[13,]   90   16   70
[14,]   40   71   25
[15,]   51   66   92
[16,]   31   10   75

Now do two lapplies to split each column into rows

现在执行两个lapply将每个列分割成行。

matrices_split_by_row = lapply(matrices_split_by_col, function(mat){

  lapply(1:4, function(row){
    mat[((row-1)*3+1):((row-1)*3+4),]
  })

})

Unlist the result:

Unlist结果:

matrices_split_by_row_and_col = unlist(matrices_split_by_row,recursive=FALSE)

Check result:

检查结果:

> matrices_split_by_row_and_col[[2]]
     [,1] [,2] [,3]
[1,]    1  116   70
[2,]   32   61   11
[3,]   88    1   60
[4,]  119   32   75

Oops, this gives the matrices going down the columns first, but anyway, you can modify the code and turn it into a function if you want, using the underlying logic.

噢,这让矩阵先从列开始,但不管怎样,你可以修改代码,如果你想,可以把它转换成一个函数,使用底层逻辑。

#5


2  

Using my limited regular programming knowledge, I came up with following code:

利用有限的常规编程知识,我提出了以下代码:

matsplitter = function(mat, submatr, submatc){
    matr = dim(mat)[1]
    matc = dim(mat)[2]
    mats_per_row=matc/submatc

    submat = array(NA, c(submatr,submatc,matr*matc/(submatr*submatc)))

    cur_submat=1; k=0
    i=j=a=b=1

    while(TRUE){
        submat[i,j,cur_submat+k] = mat[a,b]

        j=j+1
        if(j>submatc){j=1; k=k+1; if(k>(mats_per_row-1)){k=0; i=i+1; if(i>submatr){i=1;cur_submat=cur_submat+mats_per_row;}}}

        b=b+1
        if(b>matc){b=1;a=a+1; if(a>matr){break};}
    }
    submat
}

#6


1  

Modification of @MrFlick's answer:

修改@MrFlick的回答是:

matsplitter<-function(M, r, c) {
  simplify2array(lapply(
    split(M, interaction((row(M)-1)%/%r+1,(col(M)-1)%/%c+1)),
    function(x) {dim(x) <- c(r,c); x;}
  ))
}