2013-05-14 20 views
8

Po sprytnym lapply, pozostaję z listą 2-wymiarowych macierzy.Funkcjonalny sposób na układanie list macierzy 2d w macierz 3D

Na przykład:

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

Chciałbym kolei, że na 3-wymiarowej tablicy:

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

Oczywiście, jeśli używam replikacji można po prostu włączyć simplify, ale sapply nie upraszcza poprawnie wyniku, a stack kończy się niepowodzeniem. do.call(rbind,mylist) zamienia je na matrycę 2d zamiast na tablicę 3d.

Mogę to zrobić za pomocą pętli, ale szukam schludnego i funkcjonalnego sposobu na jej obsługę.

Najbliższa droga mam wymyślić to:

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

ale czuję, że to nieeleganckie (ponieważ demontuje a następnie scala atrybuty tablicy wektorów i wymaga wiele badań, aby bezpieczny (na przykład, że wymiary każdego elementu są takie same).

+3

tam pakiet abind – baptiste

+2

nie zgadzam się, że „najbliższa droga” jest nieeleganckie, a ja dalej nie zgadzają się, że potrzebuje „dużo” testów. Jest to poprawne i potrzebujesz albo 'do.call (c, test)' albo 'unlist (test)', a potem to jest całkowicie proste. –

+0

@Dwin być może jestem zbyt twardy na mój własny kod. Ale korzystanie z podstaw wektorów/macierzy zawsze mnie denerwuje. Podejrzewam jednak, że to nie może być straszne rozwiązanie. –

Odpowiedz

6

można użyć pakietu abind a następnie użyć do.call(abind, c(test, along = 3))

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

Lub możesz użyć simplify = 'array' w połączeniu z numerem sapply (zamiast lapply). simplify = 'array' nie jest taka sama jak simplify = TRUE, jak to zmienić argument higher w simplify2array

np

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

Pierwszym argumentem dla 'abind' może być lista, więc nie potrzebujesz' do.call': 'testArray <- abind (test, along = 3)' – cbeleites

+0

Zaznaczanie tej odpowiedzi, ponieważ uderza zarówno w 'abind 'i', simplify up = 'array''. –

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

lub jeśli nie wiem oczekiwaną wielkość wyprzedzeniem:

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

Twoja poprzednia odpowiedź została już włączona do mojej (twoja nowa odpowiedź jest bardzo zadowalająca) – mnel

+1

@mnel, dziękuję, właśnie zauważyłem użycie 'uproszczenia =" tablicy "' ostatnio w dokumentacji do 'sapply', a następnie zobaczyłem że był również w 'Replicate'. Ale błędnie przeczytałem komentarz PO dotyczący * nie * używania Replikacji, dlatego usunąłem pierwszą odpowiedź po ponownym przeczytaniu. –

+1

@RicardoSaporta 'uproszczenie =" tablica "' faktycznie rozwiąże mój problem 9 razy na 10. Dzięki! –

2

Tablica jest po prostu atomowym vec tor z wymiarami. Każdy z komponentów macierzy test jest tak naprawdę tylko wektorem z wymiarami. Stąd najprostszym rozwiązaniem, jakie mogę sobie wyobrazić, jest rozwinięcie listy test w wektor i przekonwertowanie jej na tablicę przy użyciu array i odpowiednio podanych wymiarów.

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 

Jeśli nie chcesz hardcode wymiary, musimy poczynić pewne założenia, ale można łatwo wypełnić w wymiarze od test, np

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

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

ta zakłada, że ​​wymiary każdego składnika są takie same, ale nie widzę w jaki sposób umieścić sub-macierzy do tablicy 3-d jeśli warunek ten nie wytrzymuje.

To może wydawać się odstręczające, aby rozwinąć listę, ale jest to po prostu wykorzystanie tego, jak R obsługuje macierze i tablice jako wektory z wymiarami.

+0

@RicardoSaporta Sortuj tak. Przepraszam; Szukałem odpowiedzi używając 'tablica' i nie widziałem żadnych, i chcąc podać wyjaśnienie dodałem odpowiedź. Zauważ, że masz błąd w pierwszej porcji - powinno to być 'c (dim (test [[1]]), 5)' –

+0

brak potu, a na pewno twoja odpowiedź oferuje znacznie więcej wyjaśnienia niż moje. Dzięki za połów, zredagowałem mój błąd –

+0

Jeśli chciałbyś zmienić kształt tablicy 3d, aby utworzyć pierwszy wymiar tablicy odpowiadający długości listy, linia ta zrobiłaby to: testMat <- aperm (tmp2, c (3,1,2)) –

10

Spróbuj tego:

simplify2array(test) 
+0

Dzięki, zadziałało dla mnie, gdy używałem wyniku "foreach" –