简体   繁体   中英

How to combine R matrices of a different size by growing rows

Suppose I have 3 matrices

A

1 0 0
0 1 0
0 1 0

B

0 0 0 1
1 0 0 0

C

0 0 0 0 1
0 1 0 0 0
1 0 0 0 0

Notice that each matrix only has one "1" per row. I need to keep this property. I want to now make a matrix that's (3 + 4 + 5)=12 columns wide and (3 + 2 + 3) = 8 rows deep that looks like this:

     1  2  3  4  5  6  7  8  9  10 11 12 
     ----------------------------
1    1  0  0  0  0  0  0  0  0  0  0  0 
2    0  1  0  0  0  0  0  0  0  0  0  0
3    0  1  0  0  0  0  0  0  0  0  0  0
4    0  0  0  0  0  0  1  0  0  0  0  0
5    0  0  0  1  0  0  0  0  0  0  0  0
6    0  0  0  0  0  0  0  0  0  0  0  1
7    0  0  0  0  0  0  0  0  1  0  0  0
8    0  0  0  0  0  0  0  1  0  0  0  0

While I have 3 matrices here, what I actually have is a list mylist which has an arbitrary number of matrices of different sizes. The only rule is that for each matrix, each row only has one "1".

Before anyone asks, this is not hw, I'm trying to combine mutation matrices in my data that are all 0/1 (where the columns represent a missense substitution for that amino acid).

Is there also a way that I can bounce around? For instance, suppose I wanted A1, A3, B1, C1, C3, ... (where the first letter represents the matrix and the number represents the column)? Or would i have to make the matrix first and then just reorder?

You can use Reduce .

combine <- function(A, B) {
  rbind(cbind(A, array(0, dim = c(nrow(A), ncol(B)))),
        cbind(array(0, dim = c(nrow(B), ncol(A))), B))
}
D <- Reduce(combine, list(A,B,C))

Example

A <- structure(c(1, 0, 0, 0, 1, 1, 0, 0, 0), .Dim = c(3L, 3L))
B <- structure(c(0, 1, 0, 0, 0, 0, 1, 0), .Dim = c(2L, 4L))
C <- structure(c(0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0), .Dim = c(3L,5L))
D <- Reduce(combine, list(A,B,C))

      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
 [1,]    1    0    0    0    0    0    0    0    0     0     0     0
 [2,]    0    1    0    0    0    0    0    0    0     0     0     0
 [3,]    0    1    0    0    0    0    0    0    0     0     0     0
 [4,]    0    0    0    0    0    0    1    0    0     0     0     0
 [5,]    0    0    0    1    0    0    0    0    0     0     0     0
 [6,]    0    0    0    0    0    0    0    0    0     0     0     1
 [7,]    0    0    0    0    0    0    0    0    1     0     0     0
 [8,]    0    0    0    0    0    0    0    1    0     0     0     0

Robert's answer is more pleasantly succinct, but nevertheless i'll present an alternative presentation. First, the input

A<-matrix(c(1,0,0,0,1,1,0,0,0), ncol=3)
B<-matrix(c(0,1,0,0,0,0,1,0), ncol=4)
C<-matrix(c(0,0,1,0,1,0,0,0,0,0,0,0,1,0,0), ncol=5)

Now i'll create a helper function called dbind which works like rbind and cbind but concatenates the matrix along the "diagonal" filling the rest with 0. Here's the function which can take an arbitrary number of matrices

dbind<-function(...) {
    dots<-list(...)
    dm<-sapply(dots, dim)
    dx<-rowSums(dm)
    of<-apply(dm,1,function(x) cumsum(c(0,x[-length(x)])))
    r<-array(0, dim=dx)
    Map(function(d,i) {
        r[sweep(which(!is.na(d), arr.ind=T), 2, i, `+`)] <<- d
    }, dots, split(of, 1:nrow(of)))
    r
}

and then you call it with

dbind(A,B,C)

#      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
# [1,]    1    0    0    0    0    0    0    0    0     0     0     0
# [2,]    0    1    0    0    0    0    0    0    0     0     0     0
# [3,]    0    1    0    0    0    0    0    0    0     0     0     0
# [4,]    0    0    0    0    0    0    1    0    0     0     0     0
# [5,]    0    0    0    1    0    0    0    0    0     0     0     0
# [6,]    0    0    0    0    0    0    0    0    0     0     0     1
# [7,]    0    0    0    0    0    0    0    0    1     0     0     0
# [8,]    0    0    0    0    0    0    0    1    0     0     0     0

Here is a way to combine using bdiag function from the Matrix package

If your matrices are in a list

library(Matrix)

l <- list(A,B,C)

as.matrix(bdiag(l))

The magic package includes a bespoke function adiag() which does exactly what you are looking for. The original application was for high-dimensional magic hypercubes but it works nicely for regular matrices and preseves dimnames. Also, one can control the pad value. Use-case:

    > library(magic)
    > A <- structure(c(1, 0, 0, 0, 1, 1, 0, 0, 0), .Dim = c(3L, 3L))
    > B <- structure(c(0, 1, 0, 0, 0, 0, 1, 0), .Dim = c(2L, 4L))
    > C <- structure(c(0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0), .Dim = c(3L,5L))
    > adiag(A,B,C)
         [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]
    [1,]    1    0    0    0    0    0    0    0    0     0     0     0
    [2,]    0    1    0    0    0    0    0    0    0     0     0     0
    [3,]    0    1    0    0    0    0    0    0    0     0     0     0
    [4,]    0    0    0    0    0    0    1    0    0     0     0     0
    [5,]    0    0    0    1    0    0    0    0    0     0     0     0
    [6,]    0    0    0    0    0    0    0    0    0     0     0     1
    [7,]    0    0    0    0    0    0    0    0    1     0     0     0
    [8,]    0    0    0    0    0    0    0    1    0     0     0     0
    > 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM