配列(array)の書き方がよくわからんかったのでメモ

行列でもたまに混乱するのに、更に次元が上がった多次元配列になんてなるともう???ってなる低能です。もちろん、配列を使うのではなくて、"行列を要素に持つリスト"を使うのも手かとは思いますが、arrayの方が便利そうだったので、行列を一次元だけあげた3次元配列を例にちょっとメモっておく。

配列(array)を作成するにはarray関数を使っておけという感じ。matrix関数と同じように第一引数には要素の値を入れて、第二引数には各次元(1次元(行)、2次元(列)、3次元…)の要素数をベクトルで指定しておく。ここでは全12要素を持つ配列(2(1次元目、行)×2×2(2次元目、列)×3(3次元目))を作成。

> a <- array(1:12, c(2, 2, 3))
> a
, , 1

     [,1] [,2]
[1,]    1    3
[2,]    2    4

, , 2

     [,1] [,2]
[1,]    5    7
[2,]    6    8

, , 3

     [,1] [,2]
[1,]    9   11
[2,]   10   12

以下では、上の結果(画面)に表示されている行列(3次元目の各要素)のことを、見たまんま"行列"と呼ぶ。


多次元配列の要素数を取得するには、length・nrow/ncol・dim関数を使うのが良さげ。特に3次元目(以降)の要素数を取得するにはdim関数がMUSTっぽい。ちなみにncol/nrow関数も中ではdim関数を呼んで1・2要素目を返却しているだけ。また、length関数を用いると"全要素数(dim関数の結果の全要素の積)"がGETできる。

> nrow(a)
[1] 2
> ncol(a)
[1] 2
> dim(a)
[1] 2 2 3
> length(a)
[1] 12

操作は行列の拡張だと思っておけばよくて、[]関数の中に[1次元目のインデックス,2次元目のインデックス,3次元目のインデックス、…]を指定すればOK。
インデックスを省略した場合は行列同様に該当する次元の"全要素"が抽出されてくる。

> #(各行列の)一行目の取得
> a[1,,]
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    3    7   11
> #(各行列の)二列目の取得
> a[,2,]
     [,1] [,2] [,3]
[1,]    3    7   11
[2,]    4    8   12
> #1つ目の行列の取得
> a[,,1]
     [,1] [,2]
[1,]    1    3
[2,]    2    4

apply関数も全次元に対して適用できるようになっている。

> #全行列の行毎の和
> apply(a, 1, sum)
[1] 36 42
> #全行列の列毎の和
> apply(a, 2, sum) 
[1] 33 45
> #全行列毎の和
> apply(a, 3, sum)
[1] 10 26 42

以下の書き方は少々混乱するが、多次元配列の処理には超絶有効なので覚えておかねばならない。

> #行と列の組合せ毎 = 各行列要素毎の和(1,2を入れ替えると転置した結果に)
> apply(a, c(1,2), sum)
     [,1] [,2]
[1,]   15   21
[2,]   18   24
> apply(a, c(2,1), sum)
     [,1] [,2]
[1,]   15   18
[2,]   21   24
> #三次元方向と列の組合せ毎(=各行列の列毎)の和
> apply(a, c(2,3), sum)
     [,1] [,2] [,3]
[1,]    3   11   19
[2,]    7   15   23
> #三次元方向と行の組合せ毎(=各行列の行毎)の和
> apply(a, c(1,3), sum)
     [,1] [,2] [,3]
[1,]    4   12   20
[2,]    6   14   22

一応、printデバッグ的に画面に出力すれば結果の確認はできる。

> apply(a, c(1,2), function(x){print(x);1} )
[1] 1 5 9
[1]  2  6 10
[1]  3  7 11
[1]  4  8 12
     [,1] [,2]
[1,]    1    1
[2,]    1    1

apermとdim関数を組み合わせてうまく行列になおしたりする操作方法も覚えておきたいところだが、これは慣れるのに時間がかかりそうだ。
apermは第二引数に次元の組み合わせ(この場合、三次元なので1,2,3の並び)を指定し、配列の変形を行う。

> #aそのまま
> aperm(a, c(1,2,3))
, , 1

     [,1] [,2]
[1,]    1    3
[2,]    2    4

, , 2

     [,1] [,2]
[1,]    5    7
[2,]    6    8

, , 3

     [,1] [,2]
[1,]    9   11
[2,]   10   12

> #行と列を入れ替えた結果(要するに各行列の転置)
> aperm(a, c(2,1,3))
, , 1

     [,1] [,2]
[1,]    1    2
[2,]    3    4

, , 2

     [,1] [,2]
[1,]    5    6
[2,]    7    8

, , 3

     [,1] [,2]
[1,]    9   10
[2,]   11   12

これをうまく利用すると、以下のように配列aを画面に表示されてるまんま下に合体させて行列に直すことができる。
何言ってるのか書いてる自分でもよくわからなくなってきたが、結果を見れば一発だ。

> aa <- aperm(a, c(1,3,2))
> dim(aa) <- c(6,2)
> aa
     [,1] [,2]
[1,]    1    3
[2,]    2    4
[3,]    5    7
[4,]    6    8
[5,]    9   11
[6,]   10   12
||< 

各次元に名前をつけるにはdimnames関数を使う。
>|r|
> dimnames(a) <- list(1:2, letters[1:2], LETTERS[1:3])
> a
, , A

  a b
1 1 3
2 2 4

, , B

  a b
1 5 7
2 6 8

, , C

   a  b
1  9 11
2 10 12