Rでデータベースに似たデータを取り扱う際には、データフレームを使うことがおおい。JavaはPHPなどのプログラミング言語では、層別に集計作業をする際にはデータベース上のSQLか、データを読み込んでから、ループを使って集計作業をする。
Rではデータフレームに対してサブセットやby()を適用して簡単に層別集計作業を実行できる。この記述方法はR独特であり、慣れるまでは間違いも多いし、記述にもとまどう。また同じ結果を戻すことができる記述方法も複数ある。
boneデータを利用して層別集計作業の記述方法をまとめておく。bone$spnbmdに対してcut()を実行してあらかじめカテゴリに分ける。
> bone$age_cat <- cut(bone$age, breaks=seq(9, 26, 1), right=FALSE, ordered_result=TRUE) > head(bone) idnum age gender spnbmd age_cat 1 1 11.70 male 0.018080670 [11,12) 2 1 12.70 male 0.060109290 [12,13) 3 1 13.75 male 0.005857545 [13,14) 4 2 13.25 male 0.010263930 [13,14) 5 2 14.30 male 0.210526300 [14,15) 6 2 15.30 male 0.040843210 [15,16) |
次にage_catごとにspnbmdの平均値を求める。まずbyを利用してみる。
> by(bone$spnbmd, bone$age_cat, mean) bone$age_cat: [9,10) [1] 0.04363858 --------------------------------------------------------- bone$age_cat: [10,11) [1] 0.05542119
byは第一パラメータに集計作業の対象となる項目、第二パラメータが層別の項目、第三パラメータが処理をする関数となる。上記の例ではbone$spnbmdに対してmean()関数を適用する。その際にbone$age_catで層別するように指示している。
今回は処理の対象をベクトルとしたがデータフレームにすることも可能である。この場合処理の内容もデータフレームを対象とする関数を指定する。
> by(bone, bone$age_cat, summary) bone$age_cat: [9,10) idnum age gender spnbmd age_cat Min. : 9.0 Min. :9.400 female:8 Min. :0.003827 [9,10) :12 1st Qu.:110.8 1st Qu.:9.688 male :4 1st Qu.:0.016003 [10,11): 0 Median :268.0 Median :9.800 Median :0.026558 [11,12): 0 Mean :234.8 Mean :9.750 Mean :0.043639 [12,13): 0 3rd Qu.:368.8 3rd Qu.:9.800 3rd Qu.:0.073463 [13,14): 0 Max. :384.0 Max. :9.950 Max. :0.100025 [14,15): 0 (Other): 0 --------------------------------------------------------- bone$age_cat: [10,11) idnum age gender spnbmd age_cat Min. : 4.0 Min. :10.00 female:15 Min. :-0.04276 [10,11):27 1st Qu.: 59.0 1st Qu.:10.18 male :12 1st Qu.: 0.02709 [9,10) : 0 Median :127.0 Median :10.55 Median : 0.04810 [11,12): 0 Mean :156.7 Mean :10.47 Mean : 0.05542 [12,13): 0 3rd Qu.:257.5 3rd Qu.:10.65 3rd Qu.: 0.08445 [13,14): 0 Max. :335.0 Max. :10.95 Max. : 0.16695 [14,15): 0 (Other): 0
次にaggregate()を使ってみる。
> aggregate(bone$spnbmd, mean, by=list(bone$age_cat)) Group.1 x 1 [9,10) 0.0436385764 2 [10,11) 0.0554211933 3 [11,12) 0.0643080464 4 [12,13) 0.0840868598 5 [13,14) 0.0750408580
aggregate()には第一パラメータに集計対象フィールド(bone$spnbmd), 第二パラメータが処理、byにカテゴリを指定する。
aggregateの別の書き方もある。下記の例ではformuralを利用する。利点としてはlevels()を使わないので記述がすっきりしているのと、levels()の処理時間分、短縮される。
> aggregate( spnbmd~age_cat, bone, mean) age_cat spnbmd 1 [9,10) 0.0436385764 2 [10,11) 0.0554211933 3 [11,12) 0.0643080464 4 [12,13) 0.0840868598 5 [13,14) 0.0750408580
spnbmd~age_catがformulraとなる。第二パラメータにデータソースとなるboneデータフレーム、第三パラメータが処理関数となる。
フォーミュラについては直感で理解するのが難しい。下記に説明が載っている。
- http://www.dummies.com/how-to/content/how-to-use-the-formula-interface-in-r.html
- http://lamages.blogspot.jp/2012/01/say-it-in-r-with-by-apply-and-friends.html
次にsapplyを利用してみる。
> sapply(split(bone$spnbmd, bone$age_cat),mean) [9,10) [10,11) [11,12) [12,13) [13,14) 0.0436385764 0.0554211933 0.0643080464 0.0840868598 0.0750408580 [14,15) [15,16) [16,17) [17,18) [18,19) 0.0584768210 0.0438216449 0.0273783762 0.0200704395 0.0086980910 [19,20) [20,21) [21,22) [22,23) [23,24) 0.0100798970 0.0065388599 -0.0054547589 0.0039951198 -0.0004379415 [24,25) [25,26) -0.0022293219 0.0170577215
この式ではsplit()でage_catで層別にしたbone$spnbmdをまず出力する。split()の出力結果はリストになっている。
> str(split(bone$spnbmd, bone$age_cat)) List of 17 $ [9,10) : num [1:12] 0.03137 0.06546 0.10003 0.01585 0.00383 ... $ [10,11): num [1:27] 0.10804 0.029 0.00193 0.12197 0.01484 ... $ [11,12): num [1:46] 0.0181 -0.0296 0.2199 -0.0061 0.0334 ...
Rではリストの各成分に対してlapply関数で処理を適用できる。その結果層別の平均が求められている。
リストとデータフレームの違いについては以下が詳しい。
https://sites.google.com/site/leihcrev/r/lists-and-data-frames
tapply()は層別に集計するために用意された関数である。
> tapply(bone$spnbmd, bone$age_cat, mean) [9,10) [10,11) [11,12) [12,13) [13,14) 0.0436385764 0.0554211933 0.0643080464 0.0840868598 0.0750408580 [14,15) [15,16) [16,17) [17,18) [18,19)
第二パラメータにはfactor化された変数を指定する。