5

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.

Krrr
  • 452
  • 1
  • 3
  • 15

2 Answers2

5

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.

flodel
  • 87,577
  • 21
  • 185
  • 223
  • +1 for the base approach. I was messing around with something similar, but must have been doing something wrong. If I remember correctly, I was putting `sequence` in the wrong place. – A5C1D2H2I1M1N2O1R2T1 Nov 05 '13 at 15:27
  • Thanks. I am choosing this as the answer as it uses base functions. – Krrr Nov 05 '13 at 17:49
4

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
A5C1D2H2I1M1N2O1R2T1
  • 190,393
  • 28
  • 405
  • 485
  • can you kindly edit the interleave function so that It works for the unequal number of rows as well? If I have one extra row in one matrix then I get an error message – KHAN irfan Aug 02 '17 at 13:28