配列に関する Tips 大全

配列に関する操作は、R をマスターする基本です。関連する Tips を脈絡なくできるだけ集めたいと思います。お気づきの正統派・裏技テクニックを お寄せください。一部重複はむしろ好ましいと思います。


要素ベクトルから配列を作る

> arr <- array(1:8, c(2, 2, 2))
> dim(arr) 
[1] 2 2 2       # 次元は3
> arr[, , 1]    # 要素は規則「一番左の添字がもっとも早く変化する」に従い並べられる
     [,1] [,2]
[1,]    1    3
[2,]    2    4
> arr[, , 2]
     [,1] [,2]
[1,]    5    7
[2,]    6    8

行列は次元属性 dim が長さ2の配列に他ならない

配列についていえることは、行列に対しても有効

> mm <- array(1:12, dim=c(3, 4))   # 配列としての行列(列主導で並べられる)
> mm <- array(1:12, c(3, 4))
> dim(mm)
[1] 3 4

配列のあるマージンに関数を適用 apply()

> x <- matrix(1:12, ncol=3)
> x
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12
> apply(x, 1, max) # 各行の最大値を求める
[1]  9 10 11 12
> apply(x, 2, max) # 各列の最大値を求める
[1]  4  8 12

複雑な場合の結果もよく吟味して利用すべし(2004/08/14)

> x <- array(1:12, c(2, 2, 3))
> x
, , 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

> apply(x, c(1, 2), sum)
     [,1] [,2]
[1,]   15   21
[2,]   18   24
> apply(x, c(1, 3), sum)
     [,1] [,2] [,3]
[1,]    4   12   20
[2,]    6   14   22
> apply(x, c(2, 3), sum)
     [,1] [,2] [,3]
[1,]    3   11   19
[2,]    7   15   23

配列の添字順序の変更 aperm()

x <- array(runif(5^4), c(5, 5, 5, 5))          # 4次元配列
y <- aperm(x, perm=c(2, 1, 3, 4))              # 添字順序の変更
# (y <- aperm(x, c(2, 1, 3, 4)) でも同じ)          
z <- aperm(x, perm=match(1:4, c(2, 1, 3, 4)))   # 添字順序の変更
zz <- aperm(y, perm=c(2, 1, 3, 4))             # 添字順序の変更

とすると x[i1, i2, i3, i4] は y[i2, i1, i3, i4] となる z[i1, i2, i3, i4] は x[i2, i1, i3, i4] となる。 zz と z は同じ

perm 引数を省略すると添字順序が逆順になる。つまり perm=rev(seq(length(dim(x)))) としたのと同じ

x <- matrix(runif(5^2), ncol=5)
y <- aperm(x, c(2, 1))

と置くと y は t(x) と一致

配列のベクトルとしての x 番目の要素の配列添字を取り出す関数

解--その1

Richard A. O'Keefe による r-help 記事(2003.7.31) より

   index.decode <- function (index, array) { # 3次元までの配列に対応
	jndex <- index - 1
	dimarr <- dim(array)
	ndims <- length(dimarr)
	if (ndims == 1) {
	    rbind(index)
	} else
	if (ndims == 2) {
	    rbind(jndex %% dimarr[1] + 1, jndex %/% dimarr[1] + 1)
	} else
	if (ndims == 3) {
	    rbind(jndex %% dimarr[1] + 1,
	          (jndex %/% dimarr[1]) %% dimarr[2] + 1,
	          jndex %/% (dimarr[1]*dimarr[2]) + 1)
       } else {
           stop("length(dims(array)) > 3 not yet implemented")
	}
   }
> arr <- array(rnorm(27), c(3, 3,3 )) # 3次元配列
> index.decode(3, arr)
     [,1]
[1,]    3
[2,]    1
[3,]    1
 > arr[3]           # ベクトルとしての arr の三番目の要素
[1] 0.929989
> arr[3,1,1]      # 配列としての対応添字は 3,1,1 
[1] 0.929989
> index.decode(c(3, 17, 13, 5), arr)
     [,1] [,2] [,3] [,4]
[1,]    3    2    1    2  # ベクトルとしての 3, 17, 13, 5 番目の要素の添字
[2,]    1    3    2    2
[3,]    1    2    2    1

解--その2

次元数に関係なく添え字を求める関数は,簡単に書ける(ダサイかもしれないが確実;青木繁伸 2004/08/15)

> index.decode <- function(index, x)
+ {
+ 	indx <- as.integer(n <- length(dimn <- rev(cumprod(dim(x)))))
+ 	index <- index-1
+ 	for (i in 2:n) {
+ 		indx[i-1] <- (index %/% dimn[i])
+ 		index <- index %% dimn[i]
+ 	}
+ 	indx[length(dimn)] <- index
+ 	rev(indx)+1
+ }
> x <- array(1:prod(2:8), 2:8) # 順に 1 〜 40320 を持つ配列
> index.decode(35, x) # 35 番目の要素の添え字は
[1] 1 3 2 2 1 1 1
> x[1, 3, 2, 2, 1, 1, 1] # 確かにその添え字の要素は 35 である
[1] 35
> index.decode(12345, x) # 以下同じ
[1] 1 2 2 5 1 4 3
> x[1, 2, 2, 5, 1, 4, 3]
[1] 12345
> index.decode(40320, x)
[1] 2 3 4 5 6 7 8
> x[2,3,4,5,6,7,8]
[1] 40320
> index.decode(40319, x)
[1] 1 3 4 5 6 7 8
> x[1, 3, 4, 5, 6, 7, 8]
[1] 40319

任意オフセットの行列・配列 (アドオンパッケージ Oarray)

Rの行列・配列は添字が1から始まるが、場合により0等の添字を使いたいこともある。アドオンパッケージ Oarray は任意のオフセットの行列・配列を定義することを可能にする。主な使い方は添字に0を含める、添字が例えば A[i,j], 3 <= i, j <= 10, しか必要ないとき不要なメモリーを消費しない、等が考えられる。Oarray を使うにはあらかじめパッケージをインストールし、library(Oarray) で読み込んでおく必要がある

注意: R では負の添字は「除外」を意味するので、問題が起きる。真の負の添字を使いたければ drop.negative=FALSE にする。base パッケージ中の関数 as.array は as.array.Oarray() 関数で置き換えられる

一般形

Oarray(data=NA, dim=length(data), dimnames=NULL, offset=NA,
       drop.negative=TRUE)
as.Oarray(x, offset=NA, drop.negative=TRUE)
as.array(x)
print(x)
x[i]
x[i, j, ...]
x[i, j, ..., drop=TRUE]

引数

data, dim, dimnames: array 関数と同じ意味
offset: 各添字組に対する最初の添字値のベクトル (既定値はすべて1)
drop.negative: 論理値。負の添字が「除外」を意味するかどうか (既定値はTRUEで除外)
x: 配列で、クラス Oarray でも良い
i, j, ...: x への添字引数

返り値

典型的にはクラス Oarray  を持つ、もしくは持たない配列。副配列を抽出すると単なる配列になる。Oarray オブジェクトを
指定すると Oarray  に再びなる。print 関数は Oarray オブジェクトを適切に表示する
> A <- Oarray(1:16, c(4, 4), offset=c(-2, -1))
> A
      [,-1] [, 0] [, 1] [, 2]
[-2,]     1     5     9    13
[-1,]     2     6    10    14
[ 0,]     3     7    11    15
[ 1,]     4     8    12    16
> A[-2, 0]  # 負の添字が除外を意味しないことを注意
[1] 5
> A[-2, -1]
[1] 1
> A[(-2):0, -1]
[1] 1 2 3
> A[(-2):0, (-1):1] # 副配列を取り出すと通常の配列になる (drop=FALSE をつけても同様 ?)
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11

配列にダミー次元を加える (2003.12.31)

次元の異なる二つの配列 x[i, j, k], y[i, j] に対し z[i, j, k]=x[i, j, k]+y[i, j] を計算したい時、配列の次元が異なるため z <- x + y とは出来ない。yy[i, j, k] が y[i, j] であるような仮の配列 yy を作れば z <- x + yy として(for ループなしで高速に)計算できる。

> a <- matrix(1:12, ncol=4)
> dim(a)
[1] 3 4
 
# a に長さ2の第3次元を付加する
> aa <- a%o%rep(1, 2)  
> dim(aa)
[1] 3 4 2
> aa[, , 1]              # aa[i, j, k] (k=1, 2) の値は a[i, j]
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12
> aa[, , 2]
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12
 
# 第1,2次元の真中に長さ2のダミー次元を挿入する (aperm 関数で次元順序を変更)
> aaa <- aperm(a%o%rep(1, 2), c(1, 3, 2))  # つまり aaa <- aperm(aa, c(1, 3, 2))
> dim(aaa)
[1] 3 2 4
> aaa[, 1,]  # aaa[i, j, k] (j=1, 2) の値は aa[i, k, j]、つまり a[i, k] と等しい
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12
> aaa[, 2,]
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12

配列を3次元からrbindで2次元にする(2007.09.20)

3次元のデータが扱いにくいので全部まとめたい、csvや表計算などにwrite.tableしたいときなどに、あると便利なのでつくってみました。あとで解析に使うことを前提に3次元目の値(dimname)を1列目にもつようにしてあります。変な部分やもっといい方法があったら、ご指摘ください。

ch.arr.3d2d <- function(ARR) {
if (length(dimnames(ARR)[[3]]) == 0) { #行列名なしの場合
	for (t in 1:(length(ARR[1, 1, ])-1)) {
		if (t == 1) {
			x <- cbind(rep(t, nrow(ARR)), ARR[, , t])
		}
		ARR.no <- cbind(rep(t+1, nrow(ARR)), ARR[, , t+1])
		x <- rbind(x,A RR.no)
	}
} else {
	if (length(dimnames(ARR)[[3]]) >= 1) { #行列名がある場合
		for (t in 1:(length(ARR[1,1,])-1)) {
			if (t == 1) {
				x <-  cbind(rep(dimnames(ARR)[[3]][t], nrow(ARR)), ARR[, , t])
			}
			ARR.no <-  cbind(rep(dimnames(ARR)[[3]][t+1], nrow(ARR)), ARR[, , t+1])
			x <- rbind(x, ARR.no)
		}
	} else {
		for (t in 1:(length(ARR[1, 1, ])-1)) { #行列名が不正な場合
			if(t==1){
				x <- cbind(rep(t, nrow(ARR)), ARR[, , t])
			}
			ARR.no <- cbind(rep(t+1, nrow(ARR)), ARR[, , t+1])
			x <- rbind(x, ARR.no)
		}
	paste("waring: dimnames data does not much")
	}
}
return(x)
}
arr <- array(1:8, c(2, 2, 2))
ch.arr.3d2d(arr)
  • 中程2カ所に出てくる dimnames(arr) は,dimnames(ARR) でしょう。
    length(ARR[1,1,]) などは,dim(ARR)[3] などとしましょう。
     cbind(rep(dim(a)[3], each=dim(a)[1]), apply(a, 2, c)) のような一行でもできます。
    が,しかし,dimnames(ARR)[[3]] が文字列のとき,行列にすると,他の数値データ部分も文字列になってしまいますが,それは困るのでしょうね。行列に変更するよりはデータフレームにした方が良いと思いますがいかがでしょう。
    以下は3次元目の名前付の場合にデータフレームにする実例
    > a <- array(1:24, dim=c(2, 3, 4))
    > dimnames(a)[[3]] <- paste("t", 1:4, sep="")
    > a
    , , t1
    
         [,1] [,2] [,3]
    [1,]    1    3    5
    [2,]    2    4    6
    
    , , t2
    
         [,1] [,2] [,3]
    [1,]    7    9   11
    [2,]    8   10   12
    
    , , t3
    
         [,1] [,2] [,3]
    [1,]   13   15   17
    [2,]   14   16   18
    
    , , t4
    
         [,1] [,2] [,3]
    [1,]   19   21   23
    [2,]   20   22   24
    
    > data.frame(dim3=rep(dimnames(a)[[3]], each=dim(a)[1]), dim2=apply(a, 2, c))
      dim3  dim2.1  dim2.2  dim2.3
    1   t1       1       3       5
    2   t1       2       4       6
    3   t2       7       9      11
    4   t2       8      10      12
    5   t3      13      15      17
    6   t3      14      16      18
    7   t4      19      21      23
    8   t4      20      22      24
    ----------
    > ch.arr.3d2d(a)
         [,1] [,2] [,3] [,4]
    [1,] "t1" "1"  "3"  "5" 
    [2,] "t1" "2"  "4"  "6" 
    [3,] "t2" "7"  "9"  "11"
    [4,] "t2" "8"  "10" "12"
    [5,] "t3" "13" "15" "17"
    [6,] "t3" "14" "16" "18"
    [7,] "t4" "19" "21" "23"
    [8,] "t4" "20" "22" "24"
  • 大変ありがとうございます。特に雑なところをご指摘いただいた点はすみません。ひとまずdimname(arr)は訂正してそのままでも動くようにはしました。他は消してもよいですがお邪魔でなければ後進の参考にそのままにしておこうかと思います。データフレームのほうが後々の数値の扱いには確かに良いですし、書き方も実にスマートですね。applyでcを使って整形とは感動しました。ちょっと初心者がでしゃばりましたかね。

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Google
WWW を検索 OKADAJP.ORG を検索
Last-modified: 2015-03-01 (日) 01:15:59 (1717d)