Rでデータをカテゴリ別に集計(足し上げや平均など)したいときに使える関数には tapply
やby
、stats::aggregate
があります。 ここではまずtapply
についてご紹介します。
使用するデータ
datasets::CO2
を例に使います。
Plant Type Treatment conc uptake
1 Qn1 Quebec nonchilled 95 16.0
2 Qn1 Quebec nonchilled 175 30.4
3 Qn1 Quebec nonchilled 250 34.8
4 Qn1 Quebec nonchilled 350 37.2
5 Qn1 Quebec nonchilled 500 35.3
6 Qn1 Quebec nonchilled 675 39.2
84データを持つdata frameで、最初の4列(Plant~conc)が実験環境、5列目(uptake)が結果数値です。 最初の4列のうち3列はfactor型ですが、concだけは数値型です。 factor型であることは後に表を整形するときなどに関わってきます。
tapply
tapply
を集計に使う場合に渡す引数は、
- 1つ目に集計する値が入った列を指定
- 2つ目に分類に使いたい列をListの形で指定
- 3つ目に集計関数を指定(
sum
なら省略してもよい)
となります。
2つ目の引数はList型とヘルプには書かれていますが、実はListでなくただのvectorを渡しても勝手にListにしてくれます。
1つ目に結果数値のuptake
、2つ目に分類としてType
を指定してみましょう。
tapply(CO2$uptake, CO2["Type"], sum)
Type
Quebec Mississippi
1408.8 877.1
Type
列の値ごとにuptake
を足し上げた結果を得られました。
分類を2つに増やすと、2次元の表が得られます。
tapply(CO2$uptake, CO2[c("Type", "conc")], sum) # 2次元の表
conc
Type 95 175 250 350 500 675 1000
Quebec 84.4 162.5 215.6 228.5 228.8 237.0 252
Mississippi 62.7 104.9 130.9 139.5 141.7 146.4 151
3つにすると3次元の表が得られますが、ベタに表示するとちょっと読みにくいですね。
tapply(CO2$uptake, CO2[c("Type", "conc", "Treatment")], sum) # 3次元の表
, , Treatment = nonchilled
conc
Type 95 175 250 350 500 675 1000
Quebec 45.8 90.1 112.2 121.1 118.8 124.5 129.5
Mississippi 33.9 60.6 82.6 89.7 91.8 91.6 94.8
, , Treatment = chilled
conc
Type 95 175 250 350 500 675 1000
Quebec 38.6 72.4 103.4 107.4 110.0 112.5 122.5
Mississippi 28.8 44.3 48.3 49.8 49.9 54.8 56.2
単に集計の結果をarrayに保持したいだけならこれでもいいですが、 視覚的に2次元にまとめて表示したいこともあります。 そういう場合は分類の変数を次のようにまとめて要素が2つ以内に収まるようにするとよいでしょう。
interactionを使って変数を結合
2つ以上の変数を1つにまとめるにはinteraction
を使うのがおすすめです。 Type
とTreatment
をまとめるとこうなります。
tapply(CO2$uptake, interaction(CO2$Type, CO2$Treatment), sum)
Quebec.nonchilled Mississippi.nonchilled Quebec.chilled Mississippi.chilled
742.0 545.0 666.8 332.1
interaction
は、
- 戻り値がfactor型
- 渡す変数はfactor型でなくても自動的にfactor型に変換してくれる
- factor型に変換されるときに組み合わせのすべてをlevelsに持つので、入力データに存在しない組み合わせも表示できる
という特徴があります。
3点目はどういうことかと言うと、例えば分類の情報として次のようにA市-人口
、B市-面積
、C市-世帯数
というデータがあったとします。
city property
1 A市 人口
2 B市 面積
3 C市 世帯数
このデータの2つの分類をinteraction
で1つにまとめると、A市
に対して人口
だけではなく面積
と世帯数
もあるはずだとしてそれをfactor型のlevelsに入れてくれます。その結果、この例では9つのlevelsが作られます。
[1] A市.人口 B市.面積 C市.世帯数
Levels: A市.人口 B市.人口 C市.人口 A市.世帯数 B市.世帯数 C市.世帯数 A市.面積 B市.面積 C市.面積
これはデータが部分的に得られなかったときにそれをあえて表示したい場合などに役立ちます。
ちなみに1点目のとおりinteraction
の戻り値はfactor型ですが、最初に述べたようにtapply
にはList型で渡さなくても勝手にListにしてくれますのでこのままtapply
の引数として使うことができます。
pasteを使うこともできる
「文字列として複数の変数を結合するのはpaste
じゃないの?」
という疑問をお持ちの方もいらっしゃると思います。 paste
でも同じことは可能です。こんな感じに書けます。
tapply(CO2$uptake, paste(CO2$Type, CO2$Treatment), sum)
Mississippi chilled Mississippi nonchilled Quebec chilled Quebec nonchilled
332.1 545.0 666.8 742.0
同じ結果・・・ですが並び順が先ほどと違いますね。 paste
を使うと文字列の順番で並び替えて表示されます。確かにアルファベットではMississippiはQuebecよりも前です。 しかしType
はもともとはfactor型で、levelsとしてはQuebecの方を先に持っています。
$levels
[1] "Quebec" "Mississippi"
$class
[1] "factor"
元データで何か意図があってlevelsを設定していることもありますので、その順番を保持するのが一般的にはよいと思います。
再びinteraction
では、まとめた変数を使って先ほどの3次元の表を2次元に表示してみましょう。
tapply(CO2$uptake, cbind(interaction(CO2$Type, CO2$Treatment), CO2["conc"]), sum)
conc
interaction(CO2$Type, CO2$Treatment) 95 175 250 350 500 675 1000
Quebec.nonchilled 45.8 90.1 112.2 121.1 118.8 124.5 129.5
Mississippi.nonchilled 33.9 60.6 82.6 89.7 91.8 91.6 94.8
Quebec.chilled 38.6 72.4 103.4 107.4 110.0 112.5 122.5
Mississippi.chilled 28.8 44.3 48.3 49.8 49.9 54.8 56.2
うまい具合に表示されました。
tappy
を使った集計についてのご紹介でした。