Functional way to stack list of 2d matrices into 3d matrix

After a clever lapply, I'm left with a list of 2-dimensional matrices.

For example:

set.seed(1)
test <- replicate( 5,  matrix(runif(25),ncol=5), simplify=FALSE )
> test
[[1]]
          [,1]       [,2]      [,3]      [,4]      [,5]
[1,] 0.8357088 0.29589546 0.9994045 0.2862853 0.6973738
[2,] 0.2377494 0.14704832 0.0348748 0.7377974 0.6414624
[3,] 0.3539861 0.70399206 0.3383913 0.8340543 0.6439229
[4,] 0.8568854 0.10380669 0.9150638 0.3142708 0.9778534
[5,] 0.8537634 0.03372777 0.6172353 0.4925665 0.4147353

[[2]]
          [,1]      [,2]      [,3]      [,4]      [,5]
[1,] 0.1194048 0.9833502 0.9674695 0.6687715 0.1928159
[2,] 0.5260297 0.3883191 0.5150718 0.4189159 0.8967387
[3,] 0.2250734 0.2292448 0.1630703 0.3233450 0.3081196
[4,] 0.4864118 0.6232975 0.6219023 0.8352553 0.3633005
[5,] 0.3702148 0.1365402 0.9859542 0.1438170 0.7839465

[[3]]
...

I'd like to turn that into a 3-dimensional array:

set.seed(1)
replicate( 5,  matrix(runif(25),ncol=5) )    

Obviously, if I'm using replicate I can just turn on simplify, but sapply does not simplify the result properly, and stack fails utterly. do.call(rbind,mylist) turns it into a 2d matrix rather than 3d array.

I can do this with a loop, but I'm looking for a neat and functional way to handle it.

The closest way I've come up with is:

array( do.call( c, test ), dim=c(dim(test[[1]]),length(test)) )

But I feel like that's inelegant (because it disassembles and then reassembles the array attributes of the vectors, and needs a lot of testing to make safe (e.g. that the dimensions of each element are the same).

Answers


You can use the abind package and then use do.call(abind, c(test, along = 3))

library(abind)
testArray <- do.call(abind, c(test, along = 3))

Or you could use simplify = 'array' in a call to sapply, (instead of lapply). simplify = 'array' is not the same as simplify = TRUE, as it will change the argument higher in simplify2array

eg

foo <- function(x) matrix(1:10, ncol = 5)
# the default is simplify = TRUE
sapply(1:5, foo)
      [,1] [,2] [,3] [,4] [,5]
 [1,]    1    1    1    1    1
 [2,]    2    2    2    2    2
 [3,]    3    3    3    3    3
 [4,]    4    4    4    4    4
 [5,]    5    5    5    5    5
 [6,]    6    6    6    6    6
 [7,]    7    7    7    7    7
 [8,]    8    8    8    8    8
 [9,]    9    9    9    9    9
[10,]   10   10   10   10   10
# which is *not* what you want
# so set `simplify = 'array'
sapply(1:5, foo, simplify = 'array')
, , 1

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

, , 2

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

, , 3

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

, , 4

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

, , 5

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

Try this:

simplify2array(test)

test2 <- unlist(test)
dim(test2) <- c(dim(test[[1]]),5)

or if you do not know the expected size ahead of time:

dim3 <- c(dim(test[[1]]), length(test2)/prod(dim(test[[1]])))
dim(test2) <- dim3

An array is simply an atomic vector with dimensions. Each of the matrix components of test is really just a vector with dimensions too. Hence the simplest solution I can think of is to unroll the list test into a vector and convert that to an array using array and suitably supplied dimensions.

set.seed(1)
foo <- replicate( 5,  matrix(runif(25),ncol=5) )
tmp <- array(unlist(test), dim = c(5,5,5))

> all.equal(foo, tmp)
[1] TRUE
> is.array(tmp)
[1] TRUE
> dim(tmp)
[1] 5 5 5

If you don't want to hardcode the dimensions, we have to make some assumptions but can easily fill in the dimension from test, e.g.

tmp2 <- array(unlist(test), dim = c(dim(test[[1]]), length(test)))

> all.equal(foo, tmp2)
[1] TRUE

This assumes that the dimensions of each component are all the same, but then I don't see how you could put sub-matrices into a 3-d array if that condition doesn't hold.

This may seem hacky, to unroll the list, but this is simply exploiting how R handles matrices and arrays as vectors with dimensions.


Need Your Help

JQGrid - SetRowData - Returns success but no data updated

javascript jqgrid

I am trying to add/edit data into my JQGrid. Currently the first column is populated and then the other six are blank and are dependent upon the first column. So I need to add or "edit the blank" ...

How to reword a Git commit by its SHA1 hash?

git git-rebase git-commit

I have created a commit with a commit message that I want to modify. I have not published the commit, yet, so I can rewrite history safely. I can find using git log, so I know its sha1 hash. How ca...

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.