ベクトル演算とsapplyの違い
ベクトルの各要素に関数を作用させたいとき、普通は次のようにする。
1 2 3 |
> f <- function(x) x*x > f(seq(1, 5)) [1] 1 4 9 16 25 |
これを、sapply
を使って次のようにも書ける。
1 2 3 |
> f <- function(x) x*x > sapply(seq(1, 5), f) [1] 1 4 9 16 25 |
一見すると何ら違いはないように見えるが、次のような場合に違いが出てくる。
1 2 3 4 5 |
> f <- function(x) x * runif(1) > f(rep(1, 5)) [1] 0.7131735 0.7131735 0.7131735 0.7131735 0.7131735 > sapply(rep(1, 5), f) [1] 0.56394702 0.71369838 0.09517609 0.72681560 0.47276722 |
ベクトル演算の場合は、全ての要素に対して同じ演算結果が適用されるる。このためrunif(1)
が1度だけ計算され、各要素にその結果が適用されるので、全要素が同じ値になる。
sapply
の場合は、各要素に対して毎回演算が行われる、このため各要素ごとにrunif(1)
が計算されて適用されるので、各要素の値が異なる。
各要素に新規の計算結果を入れたいとき
各要素の初期値と無関係に要素に計算結果を適用したいと意図したとき、関数にダミー変数を書いておいて処理する。
例えば以下の例では、5つの要素に新たな乱数を発生させてセットしようとしている。
1 2 3 4 5 6 7 |
> f <- function(dummy) runif(1) > sapply(rep(0, 5), f) [1] 0.2031135 0.4134661 0.9572596 0.6462498 0.2886842 > f(rep(0, 5)) [1] 0.4594526 > rep(f(0), 5) [1] 0.7015942 0.7015942 0.7015942 0.7015942 0.7015942 |
sapply
の場合は期待通りの結果。この場合は関数に引数が要求されるので、dummy
という引数を与えている。
関数の引数にベクトルを入れると、その引数に対して1回だけ乱数を発生させるので、結果は1個の数値。
rep()
関数の引数にf()を適用すると、1回だけ発生した乱数がすべての要素に適用されている。
複数の引数を持つ関数の場合
次の例は、各要素それぞれに0~2の範囲の乱数をセットしている。
1 2 3 |
> f <- function(dummy, a) runif(1) * a > sapply(rep(0, 5), f, 2) [1] 0.8868690 1.0057694 1.3814147 1.4157728 0.2052112 |
sapplyに渡す関数が2つ以上の引数を持つときは、関数に続けて残りの引数を渡せばよい。