21

my question might sound trivial to quite a lot of you, but after a long internet search I still don't have an answer to the following question:

How to convert a three dimensional array to a "three dimensional" list?

Suppose I have the following:

A1 <- matrix(runif(12),4,3)
A2 <- matrix(runif(12),4,3)
A3 <- matrix(runif(12),4,3)

MyList  <- list(A1,A2,A3)

MyArray <- array(NA,c(4,3,3))
MyArray[,,1] <- A1
MyArray[,,2] <- A2
MyArray[,,3] <- A3

Is there a way to convert MyArray into a list with "the same structure" as MyList?

Thank you very much for your help! Best, Romain

RomainD
  • 301
  • 1
  • 2
  • 9

5 Answers5

26

You can use lapply:

lapply(seq(dim(MyArray)[3]), function(x) MyArray[ , , x])


# [[1]]
#           [,1]       [,2]      [,3]
# [1,] 0.2050745 0.21410846 0.2433970
# [2,] 0.9662453 0.93294504 0.1466763
# [3,] 0.5775559 0.86977616 0.6950287
# [4,] 0.4626039 0.04009952 0.5197830
# 
# [[2]]
#           [,1]      [,2]      [,3]
# [1,] 0.6323070 0.2684788 0.7232186
# [2,] 0.1986486 0.2096121 0.2878846
# [3,] 0.3064698 0.7326781 0.8339690
# [4,] 0.3068035 0.4559094 0.8783581
# 
# [[3]]
#           [,1]      [,2]      [,3]
# [1,] 0.9557156 0.9069851 0.3415961
# [2,] 0.5287296 0.6292590 0.8291184
# [3,] 0.4023539 0.8106378 0.4257489
# [4,] 0.7199638 0.2708597 0.6327383
Sven Hohenstein
  • 80,497
  • 17
  • 145
  • 168
  • Or just use a loop over the third index, tho' this is cleaner. – Carl Witthoft Nov 25 '13 at 17:20
  • +1 - And a more programmable version could use `abind::asub` as follows: `lapply(seq(dim(MyArray)[3]), asub, x = MyArray, dims = 3)`. – flodel Nov 26 '13 at 02:51
  • Thanks for the reply. But as said above, I'll go for the "dimname" preserving feature! Best, Romain – RomainD Nov 26 '13 at 06:55
  • 3
    +1 because it is way faster than plyr::alply. For a array with dims = c(50,10,1688). system.time(replicate(500, alply(myarray,3))) user: 82.840 system: 1.953 elapsed: 85.331. And system.time(replicate(500, lapply(seq(dim(myarray)[3]), function(x) myarray[,,x]))) user: 5.242 system: 0.874 elapsed: 6.155 – SwatchPuppy Aug 05 '15 at 15:55
19

There is a handy function in plyr for this:

alply(MyArray,3)
$`1`
          [,1]       [,2]       [,3]
[1,] 0.7643427 0.27546113 0.31131581
[2,] 0.6254926 0.19449191 0.04617286
[3,] 0.5879341 0.10484810 0.08056612
[4,] 0.4423744 0.09046864 0.82333646

$`2`
          [,1]      [,2]      [,3]
[1,] 0.3726026 0.3063512 0.4997664
[2,] 0.8757070 0.2309768 0.9274503
[3,] 0.9269987 0.5751226 0.9347077
[4,] 0.4063655 0.4593746 0.4830263

$`3`
          [,1]       [,2]      [,3]
[1,] 0.7538325 0.18824996 0.3679285
[2,] 0.4985409 0.61026876 0.4134485
[3,] 0.3209792 0.60056130 0.8887652
[4,] 0.0160972 0.06534362 0.2618056

You can keep the dimension names simply by adding the .dims argument:

dimnames(MyArray) <- Map(paste0, letters[seq_along(dim(MyArray))],
                                  lapply(dim(MyArray), seq))

alply(MyArray,3,.dims = TRUE)
$c1
    b
a           b1         b2        b3
  a1 0.4752803 0.01728003 0.1744352
  a2 0.7144411 0.13353980 0.1069188
  a3 0.2429445 0.60039428 0.8610824
  a4 0.9757289 0.71712288 0.5941202

$c2
    b
a            b1         b2        b3
  a1 0.07118296 0.43761119 0.3174442
  a2 0.16458581 0.65040897 0.5654846
  a3 0.88711374 0.07655825 0.7163768
  a4 0.07117881 0.79314705 0.9054457

$c3
    b
a            b1        b2         b3
  a1 0.04761279 0.5668479 0.04145537
  a2 0.72320804 0.2692747 0.74700930
  a3 0.82138686 0.3604211 0.57163369
  a4 0.53325169 0.8831302 0.71119421
joran
  • 169,992
  • 32
  • 429
  • 468
  • Hi Jordan. Thank you very much for you quick reply. Indeed, this function is what I was looking for. But since @flodel provides the "dimname" preserving feature, I'll go with his solution. Best, Romain – RomainD Nov 26 '13 at 06:52
  • @RomainD Oh, but you give up too easily! All you had to do was scan the documentation for `alply`, and you'd see (as I've shown above) that keeping the dimension names is as easy as adding `.dims = TRUE`! – joran Nov 26 '13 at 15:36
  • Hi @joran (sorry for spelling your name wrongly the first time). Thanks for your additional info. Had you provided it at first, then I would probably have picked your solution. :) – RomainD Nov 27 '13 at 12:27
7

For fun (since I'm late), here is another one that only uses base R. Like @joran's, it is programmable in the sense you can easily split along any given dimension n:

split.along.dim <- function(a, n)
  setNames(lapply(split(a, arrayInd(seq_along(a), dim(a))[, n]),
                  array, dim = dim(a)[-n], dimnames(a)[-n]),
           dimnames(a)[[n]])

identical(split.along.dim(MyArray, n = 3), MyList)
# [1] TRUE

It will also preserve all your dimnames if you have any, see for example:

dimnames(MyArray) <- Map(paste0, letters[seq_along(dim(MyArray))],
                                 lapply(dim(MyArray), seq))
split.along.dim(MyArray, n = 3)
flodel
  • 87,577
  • 21
  • 185
  • 223
3

There's a function array_tree() in the tidyverse's purrr package that does this with minimum fuss:

A1 <- matrix(runif(12),4,3)
A2 <- matrix(runif(12),4,3)
A3 <- matrix(runif(12),4,3)

MyList <- list(a1=A1, a2=A2, a3=A3)

MyArray <- purrr::array_tree(MyList)

$`a1`
           [,1]       [,2]       [,3]
[1,] 0.18895576 0.16225488 0.09941778
[2,] 0.69737985 0.01757565 0.84838836
[3,] 0.06849385 0.71726810 0.52981969
[4,] 0.83352338 0.90922401 0.55946707

$a2
          [,1]      [,2]       [,3]
[1,] 0.6498039 0.5015537 0.48840965
[2,] 0.7745612 0.4346254 0.40873822
[3,] 0.4169687 0.3634961 0.01878936
[4,] 0.3753315 0.3008145 0.94580448

$a3
          [,1]      [,2]      [,3]
[1,] 0.3967292 0.8117389 0.9184360
[2,] 0.5729680 0.9466026 0.4086640
[3,] 0.5744074 0.5913192 0.6038389
[4,] 0.4507735 0.2285655 0.8815671

As shown, it preserves names by default. In general this should be preferred to plyr::alply() (@joran's answer) since plyr is no longer under active development.

wjchulme
  • 1,928
  • 1
  • 18
  • 28
1

In the more recent versions of R (>= 4.1.0, May 2021) where apply now has a simplify= argument, this can be achieved via looping over the 3rd dimension like:

apply(MyArray, 3, identity, simplify=FALSE)

Which will also retain the list names and dimnames of each matrix.

thelatemail
  • 91,185
  • 12
  • 128
  • 188