expand.grid関数・outer関数を使って2重forループを美しく書く

twitterで「Rでfor文書いて本当にすまんかった」旨を呟いたら色々とアドバイスが貰えた。内容は「expand.grid関数やouter関数を使えばいいのではないか?」ということだったので、ちょっとメモがてらやっておく。

まずはexpand.grid関数を試してみると

> x.seq <- 1:3
> expand.grid(x.seq, x.seq)
  Var1 Var2
1    1    1
2    2    1
3    3    1
4    1    2
5    2    2
6    3    2
7    1    3
8    2    3
9    3    3
> 
> class(expand.grid(x.seq, x.seq))
[1] "data.frame"

というような結果を得られる。要するに2つのベクトルの要素の直積集合をデータフレーム型で返してくれるものらしい。マニュアルを見ると多次元でも行ける模様、便利便利。なんで

product <- c()
for(i in x.seq){
	for(j in x.seq){
		product <- c(product,i*j)
	}
}
product

こんな2重forループと同じ動作をさせるには

apply(expand.grid(x.seq, x.seq), 1, prod)

と書けばいいわけだ。簡単簡単。

実行するとどちらも

> apply(expand.grid(x.seq, x.seq), 1, prod)
[1] 1 2 3 2 4 6 3 6 9

を返してくる。


一方、outer関数は

> outer(x.seq, x.seq,function(x,y)x*y)
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    2    4    6
[3,]    3    6    9

こんな風に計算結果を行列で返してくるので、先ほどの結果と同じにしたければas.numericを噛ませるようにして

> as.numeric(outer(x.seq, x.seq,function(x,y)x*y))
[1] 1 2 3 2 4 6 3 6 9

とすればいい。outer関数は2次元限定の模様なので汎用性という意味ではexpand.grid関数の勝ちかな?