2013-05-14 4 views
8

После умного lapply у меня остался список двумерных матриц.Функциональный способ перечислить список матриц 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) превращает его в матрицу 2d, а не в 3d-массив.

Я могу сделать это с помощью петли, но я ищу аккуратный и функциональный способ справиться с этим.

Ближайший путь я придумал это:

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

Но я чувствую, что это безвкусный (потому что он разбирает, а затем собирает атрибуты массива векторов, и требует много испытаний, чтобы сделать сейф (например, что размеры каждого элемента одинаковы).

+3

есть еще abind пакет – baptiste

+2

Я не согласен, что ваш «близкий путь» некрасиво и я также не согласен, что это нужно «много» тестирования. Это явно правильно, и вам нужно либо 'do.call (c, test)', либо 'unlist (test)', и после этого это совершенно просто. –

+0

@ Dwin, может быть, я слишком сильно разбираюсь в своем коде. Но использование основ векторов/матриц всегда заставляет меня нервничать. Однако точка зрения, что это может быть не ужасное решение. –

ответ

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, как это изменит аргумент higher в simplify2array

например

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

+0

Отметить это ответ, так как он попадает в оба 'abind 'и', simplify = 'array''. –

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

+1

@mnel, спасибо, я на самом деле просто заметил использование 'simplify =" array "' недавно в документации для 'sapply', а затем увидел что он также был в «Репликации». Но я неправильно прочитал комментарий OP о * not *, используя Replicate, поэтому я удалил свой первый ответ после повторного чтения. –

+1

@RicardoSaporta 'simplify =" array "' на самом деле решит мою проблему 9 раз из 10. Спасибо! –

2

Массив просто атомный vec тор с размерами. Каждая из матричных составляющих 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-й массив, если это условие не выполняется.

Это может показаться взломанным, чтобы развернуть список, но это просто использование того, как R обрабатывает матрицы и массивы в виде векторов с размерами.

+0

@ RicardoSaporta Вид да. Сожалею; Я искал ответы с помощью 'array' и не видел, и, желая дать какое-то объяснение, я добавил ответ. Обратите внимание, что у вас есть ошибка в первом фрагменте - это должно быть 'c (dim (test [[1]]), 5)' –

+0

никакого пота вообще, и, конечно, ваш ответ предлагает немного больше объяснений, чем мой. Спасибо за улов, отредактировал мою ошибку. –

+0

Если вы хотите изменить трехмерный массив, чтобы сделать первое измерение массива, соответствующее длине списка, эта строка сделает это: testMat <- aperm (tmp2, c (3,1,2)) –

10

Попробуйте это:

simplify2array(test) 
+0

Спасибо, он работал для меня при использовании с результатом 'foreach' –

Смежные вопросы