do.call( )は関数名と引数のリストを渡して、その関数を呼び出して実行する関数です。 リストに対して一括処理をするとき、たまに登場するんですがイマイチ理解していなかったので整理のためにまとめます。
lapply( )とdo.call( )の違い
両方とも「関数とリストを渡して、一括で実行する関数」なので何となく似ている感じがしますが*1、実行している処理は異なります。
lapply( )は渡されたリストの各要素に対して個々に関数を適用し、結果をリストとして返します。第1引数に引数リスト、第2引数に関数を指定します(リストを呼び出して、それぞれの要素に関数を適用するという順序)。
下の例では、リストの要素(x
, y
)にsum( )を別個に適用し、それぞれの要素に対する結果をリストとして返しています。
> ls <- list(x = 1:3, y = 4:6) > lapply(ls, sum) $x [1] 6 $y [1] 15
do.call( )は、第1引数に関数、第2引数に引数リストを指定します(lapply( )と逆)。まず1つの関数を呼び出し、その関数に一度に全ての引数を渡して1つの結果を返します。
下の例では、sum( )を呼び出して、リストの全ての要素(1,2,...,6)を渡して、1つの和を返します。
> ls <- list(x = 1:3, y = 4:6) > do.call(sum, ls) [1] 21
関数のオプション引数を渡す方法
先程使ったリストにNAを含めてみます。
ls_NA <- list(x = c(1:3, NA), y = 4:6)
x
はNAを含んでいるので、このままsum( )を適用してもNAが返ってきてしまいます。NA以外の和を求めたければsum(na.rm = TRUE)
のようにNAを除外するための引数を指定する必要があります。
lapply( )の場合は、第3引数以降にそのまま追加するだけです。
> lapply(ls_NA, sum, na.rm=TRUE) $x [1] 6 $y [1] 15
do.call( )の場合は、c( )を使って引数をまとめる必要があります。
> do.call(sum, c(ls_NA, list(na.rm=TRUE))) [1] 21
関数のオプションを指定する引数は名前付きなので、順番が逆になっていても大丈夫です。
> do.call(sum, c(list(na.rm=TRUE), ls_NA)) [1] 21
do.call( )で環境を指定して呼び出す
do.call( )のヘルプを眺めていて、envir
という引数が気になりました。
Rの中で複数の環境を保持しておいて、その環境の中で変数(関数も含む)に異なった値を割り当てておくことができるようです。
この引数を指定することで、どの環境で保存されていた関数・リストなのか区別して呼び出してくることができます。 do.call( )のヘルプでも使われている下のような2つの環境を例に見ていきましょう。
保存した環境(env
)を呼び出すには、do.call( )内でenvir = env
を指定します。
関数を変数として渡す or 文字列として渡す
関数を引用符なし(f
)で変数として渡すか、引用符付き("f"
)で文字列として渡すかの違いについてです
変数として渡す場合、現在の環境で関数f
が存在していれば、環境env
を確認する前に直ちにf
(ここではf = x
)が呼び出されます。
これに対して、引用符がついている場合("f"
)は、引用符を外して関数名を探す前に環境env
を呼び出して、その中で関数f
を探します。なので、f=x^2
が呼び出されます。
まとめると、
do.call(f, list(A), envir = env)
だと、f= とA=2が呼び出されて"2"が返されます。 これに対し、
do.call("f", list(A), envir = env)
だと、f= とA=2が呼び出されて"4"が返されます。
環境引数(envir = env
) を指定しなければ、f
でも"f"
でも現在の環境が適用され、"2"が返されます。
リストを変数として渡す or 引用符をつけて渡す
これも関数の評価と同じです。そのままlist(A)
で渡すと、現在の環境で直ちに評価されますが、quote( ) をつけると評価が保留されて、指定された環境を呼び出してから評価されます。
つまり、
do.call(f, list(A), envir = env)
だと、f= とA=2が呼び出されて"2"が返されますが、
do.call(f, list(quote(A)), envir = env)
だと、f= とA=10が呼び出されて"10"が返されます。
おわりに
- 環境の切り替えは複雑なプログラミングを行うときは必要なのかもしれませんが、私が行う解析作業では使うことは多分なさそうです。
*1:僕だけかも