Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

interleave rows of matrix stored in a list in R

Tags:

list

r

matrix

I want to create interleaved matrix from a list of matrices.

Example input:

> l <- list(a=matrix(1:4,2),b=matrix(5:8,2))
> l
$a
     [,1] [,2]
[1,]    1    3
[2,]    2    4

$b
     [,1] [,2]
[1,]    5    7
[2,]    6    8

Expected output:

1    3
5    7
2    4
6    8

I have checked the interleave function in gdata but it does not show this behaviour for lists. Any help appreciated.

like image 413
Krrr Avatar asked Nov 05 '13 03:11

Krrr


2 Answers

Here is a one-liner:

do.call(rbind, l)[order(sequence(sapply(l, nrow))), ]
#      [,1] [,2]
# [1,]    1    3
# [2,]    5    7
# [3,]    2    4
# [4,]    6    8

To help understand, the matrices are first stacked on top of each other with do.call(rbind, l), then the rows are extracted in the right order:

sequence(sapply(l, nrow))
# a1 a2 b1 b2 
#  1  2  1  2 

order(sequence(sapply(l, nrow)))
# [1] 1 3 2 4

It will work with any number of matrices and it will do "the right thing" (subjective) even if they don't have the same number of rows.

like image 197
flodel Avatar answered Sep 27 '22 22:09

flodel


Rather than reinventing the wheel, you can just modify it to get you to your destination.

The interleave function from "gdata" starts with ... to let you specify a number of data.frames or matrices to put together. The first few lines of the function look like this:

head(interleave)
# 
# 1 function (..., append.source = TRUE, sep = ": ", drop = FALSE) 
# 2 {                                                              
# 3     sources <- list(...)                                       
# 4     sources[sapply(sources, is.null)] <- NULL                  
# 5     sources <- lapply(sources, function(x) if (is.matrix(x) || 
# 6         is.data.frame(x)) 

You can just rewrite lines 1 and 3 as I did in this Gist to create a list version of interleave (here, I've called it Interleave)

head(Interleave)
#                                                                     
# 1 function (myList, append.source = TRUE, sep = ": ", drop = FALSE) 
# 2 {                                                                 
# 3     sources <- myList                                             
# 4     sources[sapply(sources, is.null)] <- NULL                     
# 5     sources <- lapply(sources, function(x) if (is.matrix(x) ||    
# 6         is.data.frame(x)) 

Does it work?

l <- list(a=matrix(1:4,2),b=matrix(5:8,2), c=matrix(9:12,2))
Interleave(l)
#   [,1] [,2]
# a    1    3
# b    5    7
# c    9   11
# a    2    4
# b    6    8
# c   10   12
like image 34
A5C1D2H2I1M1N2O1R2T1 Avatar answered Sep 27 '22 23:09

A5C1D2H2I1M1N2O1R2T1