2013-05-14 112 views
8

巧妙的lapply之后,我剩下一列2维矩阵。将2D矩阵列表堆栈到3D矩阵的函数式方法

例如:

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]] 
... 

我希望把它转换成一个3维数组:

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

显然,如果我使用复制我就可以打开simplify,但sapply没有正确简化结果,并且stack完全失败。 do.call(rbind,mylist)将其变成二维矩阵而不是三维阵列。

我可以用循环做到这一点,但我正在寻找一个简洁而实用的方式来处理它。

我想出最接近的方法是:

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

但我认为这是个不雅的(因为它拆开,然后重新组装矢量的排列特性,并且需要大量的测试,使安全的(例如,每个元件的尺寸是相同的)。

+3

还有的abind包 – baptiste 2013-05-14 00:46:43

+2

我不同意,你最亲密的方式“是不雅的,我不同意进一步,它需要测试的“很多”。这显然是正确的,你确实需要'do.call(c,test)'或'unlist(test)',之后就完全简单了。 – 2013-05-14 01:13:17

+0

@Dwin也许我对自己的代码太难了。但利用矢量/矩阵的基本原理总是让我感到紧张。但要指出的是,这可能不是一个可怕的解决方案。 – 2013-05-14 01:32:01

回答

6

可以使用abind包,然后使用do.call(abind, c(test, along = 3))

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

或者您可以使用simplify = 'array'致电sapply(而不是lapply)。 simplify = 'array'是不一样的simplify = TRUE,因为它会改变说法highersimplify2array

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 
+1

'abind'的第一个参数可能是一个列表,所以你不需要'do.call':'testArray < - abind(test,along = 3)' – cbeleites 2013-05-14 10:31:35

+0

标记这个答案,因为它同时击中'abind '和',简化='数组'。 – 2014-01-09 19:56:11

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

,或者如果你不知道预期的大小提前:

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

你以前的回答现在已经被纳入我的了(你的新答案非常整洁) – mnel 2013-05-14 01:03:02

+1

@mnel,谢谢,我刚刚注意到最近在'sapply'文档中使用'simplify =“array'',然后看到它也在'Replicate'中。但是我误解了OP关于* not *使用Replicate的评论,因此我在重新阅读时删除了我的第一个答案。 – 2013-05-14 01:05:50

+1

@RicardoSaporta'simplify =“array”'实际上可以解决我的问题9次,满分10分。谢谢! – 2013-05-14 01:34:04

2

一个数组只是一个原子vec tor与尺寸。 test的每个矩阵组件实际上只是一个具有维度的向量。因此,我能想到的最简单的解决方案是将列表test展开到一个向量中,并使用array和适当提供的尺寸将其转换为数组。

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 

如果你不想硬编码的尺寸,我们必须作出一些假设,但可以在尺寸从test随便填,例如

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

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

这假定每个部件的尺寸都是相同的,但后来我不看你怎么可以把子矩阵为3-d阵列如果条件不成立。

这可能看起来很不习惯,展开列表,但这只是利用R如何处理矩阵和数组作为具有维度的向量。

+0

@RicardoSaporta排序的是。抱歉;我使用'array'查找答案,但没有看到任何答案,并且想要提供一些解释,我添加了一个答案。注意你在第一个块中有一个错误 - 它应该是'c(dim(test [[1]]),5)' – 2013-05-14 02:34:32

+0

根本没有出汗,当然,你的回答比我的解释提供了更多的解释。感谢您的接触,编辑我的错误 – 2013-05-14 02:37:19

+0

如果想要重塑3d数组以便使数组的第一维对应列表的长度,则此行将执行此操作:testMat < - aperm(tmp2,c (3,1,2)) – 2017-10-22 21:13:39

10

试试这个:

simplify2array(test) 
+0

谢谢,它在使用'foreach'的时候对我有效 – 2015-03-03 13:51:47