将矩阵分割成子矩阵的R函数

15

我有一个16行12列的矩阵M,我想将它分成一个包含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]
.....

我该如何创建一个通用函数matsplitter(M, r, c),将M分割成一个由每个具有r行和c列的矩阵组成的数组?

谢谢你的帮助。


现在您想要的是 https://dev59.com/H4Dba4cB1Zd3GeqPIMHW 的反转。 - MrFlick
你看过array()了吗? - Rich Scriven
@MrFlick:好的,请吧。期待您带来更多聪明的编程。 - rnso
@RichardScriven:我尝试了array(M,c(4,3)),但它只取M的第一列中的所有数字。 - rnso
1
@rnso 好的。我尽力了,就为了你。 - MrFlick
有四个复杂的答案,但没有人觉得我的问题有足够的趣味性来点赞。没关系。 - rnso
7个回答

20

如果您有一个像这样的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))

你可以将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
} 

那么。
matsplitter(mb,4,3)

将返回(输出被剪裁)

, , 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"

...

9

这里有一个使用Kronecker乘积执行相同操作的函数。为什么?因为我喜欢Kronecker乘积。额外的好处是,如果您的行和列值不能完全被输入矩阵整除,则该函数将使用NA在右侧和底部边缘填充较小的矩阵,以便您仍然可以获得数组输出。

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)
}

因此,使用原始示例:

> 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
...

但是如果你这样做:

mat_split(M,4,5)

你会得到:

, , 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

...

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

这个实现很漂亮,但当nrow(A)很大(> 10000)时可能会变慢,因为需要评估这一行代码:matlist <- split(newM, div_k)。 - yliueagle

6
使用expand.grid回答问题,使用行和列的列表来分割。可以通过不同大小的列/行块进行概括分割。
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

初始数据

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

按列拆分

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

现在需要进行两次应用以将每列拆分为行。
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),]
  })

})

取消列表形式的结果:

matrices_split_by_row_and_col = unlist(matrices_split_by_row,recursive=FALSE)

检查结果:

> 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

糟糕,这会首先按列给出矩阵,但无论如何,如果您想要,可以修改代码并将其转换为函数,使用底层逻辑。


4
修改@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;}
  ))
} 

优雅的改进。我们只是以稍微不同的顺序返回结果(我首先沿着矩阵的顶行,这个则是沿着矩阵的第一列)。 - MrFlick

3

在我的有限的常规编程知识下,我想出了以下代码:

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
}

3
你至少尝试发布代码,就有一定的勇气,这值得肯定。继续努力,你会成功的! - Beaker
你应该检查帖子的日期。感谢你在这么多月后提醒我关于这篇文章。你有一个真正的解决方案吗? - rnso
不,这只是对你花时间尝试的一种赞美。我本来在这里寻找答案,结果发现你发布的代码中有些部分对我很有用。 - Beaker
我刚意识到我忘记点赞你的帖子了,因为它确实给了我一些有用的想法。谢谢! - Beaker

1
这里是另一种使用 split.data.frame 的解决方案:
matsplitter <- function(M, r, c) {
  splitMatrix <- function(mat, nrow) {
    split.data.frame(t(mat), ceiling(1:ncol(mat)/ncol(mat)*nrow))
  }
  sapply(splitMatrix(M, c), splitMatrix, r)
}

然后该函数提供一个列表的矩阵:
res <- matsplitter(M, 4, 3)
res

  1          2          3
1 Integer,16 Integer,16 Integer,16
2 Integer,16 Integer,16 Integer,16
3 Integer,16 Integer,16 Integer,16
4 Integer,16 Integer,16 Integer,16

您可以对矩阵进行任何子集操作。例如,选择第二行第二列的块:

res[2,2]
[[1]]
     [,1] [,2] [,3] [,4]
[1,]  116   93   73   53
[2,]   29   33   32   27
[3,]   29   57   89   96
[4,]   32   14   33   85

这适用于任何指定的尺寸,即使数字不是行/列长度的倍数:
> matsplitter(M, 7, 7)
  1         2         3         4         5         6         7
1 Integer,2 Integer,4 Integer,4 Integer,2 Integer,4 Integer,4 Integer,4
2 Integer,2 Integer,4 Integer,4 Integer,2 Integer,4 Integer,4 Integer,4
3 Integer,2 Integer,4 Integer,4 Integer,2 Integer,4 Integer,4 Integer,4
4 Integer,3 Integer,6 Integer,6 Integer,3 Integer,6 Integer,6 Integer,6
5 Integer,2 Integer,4 Integer,4 Integer,2 Integer,4 Integer,4 Integer,4
6 Integer,2 Integer,4 Integer,4 Integer,2 Integer,4 Integer,4 Integer,4
7 Integer,3 Integer,6 Integer,6 Integer,3 Integer,6 Integer,6 Integer,6

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接