24. apply() ファミリー


apply() ファミリー

関数 apply() ファミリーには apply(), mapply(), lapply(), sapply(), tapply() が用意されている.一つの関数を複数のオブジェクトに適用して得られた結果をベクトルや行列,リストとして一括で返す.例えば m × n 行列 X の全ての要素に 1 を足す場合,R では繰り返し文 (for や while) を使わなくても X <- X+1 で手軽かつ高速に実現できるが,この apply() ファミリーに属する関数の機能もこれに似ている.すなわち,C や JAVA ならば複数オブジェクトを個別に与えて繰り返し文で回さなければいけないような場面でも,apply() ファミリーの関数を使えば簡潔な記述で高速に計算できる場面が出てくるということである.

関数

機能

apply(X, MARGIN, 関数, ...)

ベクトルや行列,配列の MARGIN に関数を適用し,その結果の配列かリストを返す.
ここで MARGIN = 1 ならば行に関して,MARGIN = 2 ならば列に関して,MARGIN = c(1,2) ならば各要素に対して関数を適用する.

lapply(X, 関数, ...)
sapply(X, 関数, ...)

リストに関数を適用し,lapply() は結果のリストを,sapply() は結果の「 names 属性付きのベクトル」か「 names 属性付きの行列」を返す.
ベクトルや行列にこれらを適用すると,各成分について関数を適用するのでエラいことになってしまうので注意.

tapply(X, INDEX, 関数, ...)

グループ化された変数について,グループごとに関数を適用する.INDEX は X の要素をグループに分ける因子の組み合わせのリスト (通常は文字列ベクトル) を与え,各グループに関数を適用した結果をベクトルもしくはリストで返す.

mapply(関数 F , x, y, z, ... )

sapply() の多変量版.x, y, z, はベクトルや行列などを複数個指定でき,関数 F(x, y, z, ...) の結果をベクトルのリストで返す.
例えば mapply(sum, 1:3, 4:6) ならば [ sum(1,4), sum(2,5), sum(3,6) ] = [ 5, 7, 9 ] が,mapply(sum, 1:3, 4:5) ならば [ sum(1,4), sum(2,5), sum(3,4) ] = [ 5, 7, 7 ] が返ってくる (要素の数が引数ごとで異なる場合は反復して用いられる.ただし warning メッセージが出る.).

sweep(X, MARGIN, 統計量, FUN="-", ...)

ベクトルや行列,配列の MARGIN で指定した場所から統計量を引く(関数 scale() も同様の機能).FUN="+" とすれば統計量を足す.

apply() と sweep()

行列の処理でよく使う関数が apply() である.関数 sweep() と組み合わせることで,データの基準化などを行うことも出来る.

 
 ( x <- matrix(1:8, ncol=4) )

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

 apply(x, 2, sum)                         # 各列の最大値を求める

[1]  3  7 11 15

 apply(x, c(1,2), sqrt)                   # 各要素の平方根を求める

         [,1]     [,2]     [,3]     [,4]
[1,] 1.000000 1.732051 2.236068 2.645751
[2,] 1.414214 2.000000 2.449490 2.828427

 sweep(x, 1, apply(x,1,mean))             # 行平均を引く

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

 sweep(a, 2, apply(a,2,mean))             # 列平均を引く

lapply() と sapply()

ある関数をリストの要素全てに適用したい場合は関数 lapply(),sapply() を用いる.lapply() を用いた場合は結果は各要素に関数を適用した結果の値のリストで返される.sapply() を用いた場合は結果はベクトルで返されるが,型は要素の中で「一番大きな型」に合わせられる.

 
 x <- list(a = 1:10, beta = exp(-3:3), logic = c(T,F,F,T))
 lapply(x, mean)                           # リストの三つの成分の平均値からなるリストを計算

$a                                         # 第 1 成分の和
[1] 5.5

$beta                                      # 第 2 成分の和
[1] 4.535125

$logic                                     # 論理値は整数 (TRUE=1, FALSE=0) と見なされる
[1] 0.5

 sapply(x, mean)                           # 型の大小関係で一番大きい方に合わせられる

       a     beta    logic 
5.500000 4.535125 0.500000 

 lapply(x, quantile, probs = 1:3/4)        # 結果はリスト
 sapply(x, quantile)                       # 結果は属性付きの行列

一般的には sapply() の方が計算結果を2次使用しやすい.また,sapply() のラッパー関数に replicate() というものがある.詳しくはヘルプを参照されたい.

tapply()

グループ化されたデータの処理でよく使う関数が tapply() である.最初はとっつきにくいが,カテゴリカルデータを扱うは結構役に立つ.

 
 data(warpbreaks)                         # 1台の織機当たりの反り破損の数
 attach(warpbreaks)                       # 使うデータを固定
 fc <- factor(tension)                    # 要素をグループ化
 levels(fc)                               # グループ化されているかを確認

[1] "L" "M" "H"

 tapply(breaks, fc, mean)                 # breaks の平均をグループごとに計算

       L        M        H 
36.38889 26.38889 21.66667 

mapply()

トリッキーな使い方かもしれないが,mapply() を使えば規則的なリストを簡単に作成することが出来る.

 
 mapply(rep, 1:3, 3:1)   # rep(1,3), rep(2,2), rep(3,1) を並べる

[[1]]
[1] 1 1 1

[[2]]
[1] 2 2

[[3]]
[1] 3

 mapply(rep, 1:3, 3:1)   # rep(3,1), rep(2,2), rep(1,3) を並べる

[[1]]
[1] 1 1 1

[[2]]
[1] 2 2

[[3]]
[1] 3