apply 系関数
apply 系の関数は、多数のデータに対して何かある関数f() をイッキに適用したいときに用います。もちろんfor ループなどを使って個々のデータに対してf() を適用することもできますが、apply 系関数の方が高速に計算することができます。ちょっと古いですが、ここに速度比較が載っています。
さて、本稿では以下の設定で考えます。
次のような10件のデータがあったとしましょう。
> d
school height weight
1 1 172.3 60.2
2 1 168.4 62.6
3 2 168.7 59.9
4 1 166.3 58.9
5 1 151.9 51.4
6 2 173.4 67.0
7 2 178.8 70.4
8 2 178.3 61.3
9 1 153.1 53.7
10 2 175.1 54.1
これは中学3年生(school=1)と高校3年生(school=2)合計10人の身長(height)と体重(weight)の架空データです。
このデータから以下のような集計値を計算したいとしましょう。
- 全員の身長および体重の平均が知りたい
- 全員の身長の単位をcmからmに変えたい
- 中学/高校別に身長の平均が知りたい
- 各個人のBMIが知りたい
このような要求に対してapply 系の関数を活用し、応えることができます。
具体的には
- 全員の身長および体重の平均が知りたい → apply()
- 全員の身長の単位をcmからmに変えたい → sapply()
- 中学/高校別に身長の平均が知りたい → tapply()
- 各個人のBMIが知りたい → mapply()
をそれぞれ使うことになります。順に見ていきます。
apply()
apply() は2次元データ(data.frameやmatrix)の行ごと、または列ごとの集計値をイッキに計算するときに使用します。
apply(d, c, f) の形で使います。
d: データ(data.frame, matrix など)
f: 適用する関数
c: 1 なら関数fを行方向(横向き)に、2なら列方向(縦向き)に適用
になります(説明の都合上順番が前後しました)。
上記のdata.frame d にapply() を適用し、全員の身長および体重の平均を計算してみましょう。
> apply(d, 2, mean)
school height weight
1.50 168.63 59.95
もちろん、身長だけの平均を求めるのであれば、apply() を使わなくても
> mean(d$height)
[1] 168.63
で十分です。対してapply()を使えば、身長の平均と体重の平均をイッキに計算できます。言わずもがなですが、列数がもっと多い状況を考えてみてください。いちいち各列について平均を求めるのは面倒ですし時間の無駄ですよね。
ということで、
- 単一の列(行)の平均を求めたいときはmean(d$height)
- 複数の列(行)の平均を求めたいときにはapply(d, 2, mean)
を使うとよいでしょう。
(ここでは「平均」で説明しましたが、f の部分を他の関数、例えばsum()やsd()などに変えれば和や標準偏差の計算がイッキにできます)
sapply()
sapply() はデータ(vector) の各要素に対して何らかの関数f()をイッキに適用したいときに使います。
sapply(v, f)の形で使います。
v: データ(vector)
f: 適用する関数
になります。
上記のdata.frame d にsapply() を適用し、全員の身長の単位をcmからmに変えてみましょう。
> f <- function(x) {return (x/100)} # 適用する関数f()の定義
> sapply(d$height, f)
[1] 1.723 1.684 1.687 1.663 1.519 1.734 1.788 1.783 1.531 1.751
ここでは一度関数オブジェクトfを定義してからsapply() を適用しましたが、以下のように、sapply() 内で無名関数として定義して適用することもできます。
> sapply(d$height, function(x) {return (x/100)})
[1] 1.723 1.684 1.687 1.663 1.519 1.734 1.788 1.783 1.531 1.751
この例で、あえてfor文で処理すれば、こんな感じになります。
> v <- c()
> for (i in 1:10) {
+ v<- append(v, f(d$height[i]))
+ }
> v
[1] 1.723 1.684 1.687 1.663 1.519 1.734 1.788 1.783 1.531 1.751
でもsapply() を使えばイッパツですね。
ちなみに似た働きをする関数でlapply() というものがあります。sapply() との違いは、戻り値がリストになっているということです。詳しくはこちらをご覧下さい。
tapply()
tapply() はカテゴリごとに関数を適用して集計値を求めるときに使います。
tapply(v, c, f)の形で使います。
v: データ(vector)
c: カテゴリ(vector) つまり、cの値ごとに集計する
f: 適用する関数
になります。
上記のdata.frame d にtapply() を適用し、中学(school=1)/高校(school=2)別に身長の平均を求めてみましょう。
> tapply(d$height, d$school, mean)
1 2
162.40 174.86
ということで、中学3年生の身長の平均は162.4cm、高校3年生の平均は174.9cm ということがわかりました。データをいちいち並べ直さなくていいからすごく便利ですよね。
mapply()
mapply() は複数列のデータに関数を適用し、変換するときに使います。
mapply(f, d1, d2, ...)の形で使います。
f: 適用する関数
d1, d2, ... : データ(vector)
になります。
上記のdata.frame d にmapply() を適用し、各個人のBMI を求めてみましょう。
準備として、上記sapply() を使って単位を変換した身長をdata.frame d に付け加えておきます。
data.frame に列を付け加えるにはcbind() を使います。
> m <- sapply(d$height, function(x) {return (x/100)})
> m
[1] 1.723 1.684 1.687 1.663 1.519 1.734 1.788 1.783 1.531 1.751
> d <- cbind(d,m) # vector m をdata.frame d の第4列として付け加える
> d
school height weight m
1 1 172.3 60.2 1.723
2 1 168.4 62.6 1.684
3 2 168.7 59.9 1.687
...(以下省略)
準備が整いました。BMI(下記サイト参照)を求めます。ちなみにBMIは以下の式で求めることができます。ここで$w$は体重(単位kg)、$h$は身長(単位m)です。
BMI = \frac{w}{h^2}
> mapply(function(x, y) {return(x/(y^2))}, d$weight, d$m)
[1] 20.27804 22.07446 21.04731 21.29761 22.27653 22.28315 22.02103 19.28225 22.90994 17.64513
ここでも関数として無名関数を引数中で定義しました。
BMI は2変数から計算されますが、3変数以上から計算する必要があればmapply(f, d1, d2, d3, ...) と引数を増やすことができます。
まとめ
apply 系関数を使えば、data.frame やmatrix で与えられたデータに対し、for 文などの繰り返しを使うことなく、イッキに処理を行うことができます。ぜひ使いこなせるようになりたいですね。
参考文献
apply系関数の使い方 : 竹中明夫先生のページ
StatsBeginner: 初学者の統計学習ノート : for、apply、ベクトル演算の処理速度の比較
e-ヘルスネット : BMIについての説明
トップページはこちら