J'ai une matrice M avec 16 lignes et 12 colonnes et je souhaite la scinder en un tableau de 16 matrices, chacune comportant 4 lignes et 3 colonnes. Je peux le faire manuellement en:
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]
.....
Mais comment puis-je créer une fonction générique matsplitter (M, r, c) qui divise M en un tableau de matrices, chacune avec r lignes et c colonnes?
Merci de votre aide.
Si vous avez un tableau 16x12 comme celui-ci
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))
Vous pouvez définir matsplitter
comme
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
}
Ensuite
matsplitter(mb,4,3)
retournera (sortie coupée)
, , 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"
...
Voici une fonction qui utilise les produits Kronecker pour faire la même chose. Pourquoi? Parce que j'aime les produits Kronecker. L'avantage ici est que si les valeurs de vos lignes et de vos colonnes ne se divisent pas de manière égale dans votre matrice d'entrée, cette fonction extirpe les matrices plus petites situées aux extrémités droite et inférieure de NA afin que vous puissiez toujours avoir une sortie de tableau.
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)
}
Donc, en utilisant l'exemple original:
> 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
...
mais si vous faites ceci:
mat_split (M, 4, 5)
vous recevez:
, , 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
...
Un autre ajout utile pourrait être d'avoir l'option de produire une liste de matrices au lieu d'un tableau, ce qui signifie que vous n'auriez pas à remplir de NA.
Répondez en utilisant expand.grid
, en utilisant une liste de lignes et de colonnes à scinder. Peut généraliser à la division par blocs de colonnes/lignes de tailles différentes.
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))
Modification de la réponse de @ 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;}
))
}
Donnée initiale
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 par colonnes
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
Maintenant, faites deux lapplies pour diviser chaque colonne en lignes
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),]
})
})
Désinscrire le résultat:
matrices_split_by_row_and_col = unlist(matrices_split_by_row,recursive=FALSE)
Vérifier le résultat:
> 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
Oups, cela donne les matrices en commençant par les colonnes, mais vous pouvez quand même modifier le code et le transformer en fonction si vous le souhaitez, en utilisant la logique sous-jacente.
En utilisant mes connaissances limitées en programmation régulière, j'ai créé le code suivant:
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
}
Voici une autre solution utilisant 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)
}
Ensuite, la fonction fournit une matrice de listes:
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
Et vous pouvez créer une partie de la matrice de votre choix. Par exemple, le bloc de la deuxième rangée et de la deuxième colonne:
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
Et cela fonctionne avec toutes les dimensions spécifiées, même lorsque le nombre n'est pas multiple de la longueur de la ligne/colonne:
> 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