<< 目次へ
最終更新日 : 2015.4.29
reshape2によるクロス集計(Excelピボットテーブルと同じ操作を行う)
Rでクロス集計を行い場合、table()という関数が用意されているので簡単に頻度集計を行うことができます。しかし、Excelのピボットテーブルに慣れ親しんだ人にとっては「行ラベル」に**を、「列ラベル」に**を、そして「値」のところに「データの個数」、「合計」、「平均」などをドラッグする、ということをしたいことでしょう。しかし、残念ながらRに実装されているtable()は、Excelでいうところの「データの個数」を集計することしかできません。
ここでは前述したようなExcelのピボットテーブルと同じような操作をR上でも可能な reshape2 というパッケージを紹介することにします。この reshape2 に含まれる melt() と acast() という関数を使っていきます。ただし、単に「合計」を求めるだけであれば、例えば xtabs() という関数も使えます。
練習としてdplyrによるデータフレーム操作でも使ったCO2というデータを利用します。
> 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 7 Qn1 Quebec nonchilled 1000 39.7 8 Qn2 Quebec nonchilled 95 13.6 9 Qn2 Quebec nonchilled 175 27.3 10 Qn2 Quebec nonchilled 250 37.1 (以下省略)
■table()によるクロス集計
まずは table() を使っての頻度集計(Excelの「データの個数」)をしてみましょう。
> table(CO2$Type) # 1変量の頻度集計 Quebec Mississippi 42 42 > table(CO2$Type, CO2$Treatment) # 2変量の頻度集計(クロス集計) nonchilled chilled Quebec 21 21 Mississippi 21 21 > table(CO2$Type, CO2$Treatment, CO2$Plant) # 3変量の頻度集計 , , = Qn1 nonchilled chilled Quebec 7 0 Mississippi 0 0 , , = Qn2 nonchilled chilled Quebec 7 0 Mississippi 0 0 (以下省略)
2変量の頻度集計(クロス集計)はともかく、3変量の頻度集計になると見づらいですね。そういうときは ftable() という関数があるので、これを利用しましょう。ちなみに、3変量の頻度集計は、3次元の行列形式で結果が保持されています(リスト形式のように見えますが、行列形式です)。
## 上で作成した3変量の頻度集計の結果をtblへ入れておく > tbl <- table(CO2$Type, CO2$Treatment, CO2$Plant) ## ftableを使って保持形式を変える(でも横幅が広がって見にくいということもある) > ftable(tbl) Qn1 Qn2 Qn3 Qc1 Qc3 Qc2 Mn3 Mn2 Mn1 Mc2 Mc3 Mc1 Quebec nonchilled 7 7 7 0 0 0 0 0 0 0 0 0 chilled 0 0 0 7 7 7 0 0 0 0 0 0 Mississippi nonchilled 0 0 0 0 0 0 7 7 7 0 0 0 chilled 0 0 0 0 0 0 0 0 0 7 7 7 ## そういう場合は一工夫する↓ > tbl <- table(CO2$Plant, CO2$Type, CO2$Treatment) > ftable(tbl) nonchilled chilled Qn1 Quebec 7 0 Mississippi 0 0 Qn2 Quebec 7 0 Mississippi 0 0 Qn3 Quebec 7 0 Mississippi 0 0 Qc1 Quebec 0 7 Mississippi 0 0 Qc3 Quebec 0 7 Mississippi 0 0 Qc2 Quebec 0 7 Mississippi 0 0 Mn3 Quebec 0 0 Mississippi 7 0 Mn2 Quebec 0 0 Mississippi 7 0 Mn1 Quebec 0 0 Mississippi 7 0 Mc2 Quebec 0 0 Mississippi 0 7 Mc3 Quebec 0 0 Mississippi 0 7 Mc1 Quebec 0 0 Mississippi 0 7
■xtabs()によるクロス集計
xtabs()でもtable()と全く同じことができます。これはExcelピボットテーブルでいう「データの個数」に該当するものですね。
> xtabs( ~ CO2$Type) # 1変量の頻度集計 CO2$Type Quebec Mississippi 42 42 > xtabs( ~ CO2$Type+CO2$Treatment) # 2変量の頻度集計 CO2$Treatment CO2$Type nonchilled chilled Quebec 21 21 Mississippi 21 21
ある変数の「合計」を各セルに計算したい場合は以下のようにします。しかし、xtabs()では「平均」などを計算することはできません。
> xtabs(CO2$uptake ~ CO2$Type) # 1変量の「合計」の集計 CO2$Type Quebec Mississippi 1408.8 877.1 > xtabs(CO2$uptake ~ CO2$Type + CO2$Treatment) # 2変量の「合計」の集計 CO2$Treatment CO2$Type nonchilled chilled Quebec 742.0 666.8 Mississippi 545.0 332.1
■reshape2を活用したクロス集計
このページの冒頭でも述べましたが、reshape2というパッケージには melt() と acast() という関数が含まれています。この2つを用いることで、(今までの説明書きになぞらえて表現すると)1変量の「平均」の集計や2変量の「平均」の集計などができます(なおacastでは1変量の集計ができない?みたい・・・)。それでは実際にその使い方を紹介していきましょう。
## パッケージのインストール > install.packages("reshape2") ## パッケージの読み込み > library(reshape2) ## 「おまじない」のような前処理と思えばよい > mCO2 <- melt(CO2, # データフレーム id.vars=c("Plant", "Type", "Treatment"), # グループ集計のキーになる変数 measure.vars="uptake", # 実際に集計する「値」の変数 variable.name="sn", # テキトーな名前でOK value.name="sv") # これもテキトーでOK ## データの中身を確認 > mCO2 Plant Type Treatment sn sv 1 Qn1 Quebec nonchilled uptake 16.0 2 Qn1 Quebec nonchilled uptake 30.4 3 Qn1 Quebec nonchilled uptake 34.8 4 Qn1 Quebec nonchilled uptake 37.2 5 Qn1 Quebec nonchilled uptake 35.3 6 Qn1 Quebec nonchilled uptake 39.2 7 Qn1 Quebec nonchilled uptake 39.7 (以下省略) > acast(mCO2, Type~Treatment , sum) # 2変量の「合計」 xtabsと同じ結果 Using sv as value column: use value.var to override. nonchilled chilled Quebec 742 666.8 Mississippi 545 332.1 > acast(mCO2, Type~Treatment , mean) # 2変量の「平均」の集計 Using sv as value column: use value.var to override. nonchilled chilled Quebec 35.33333 31.75238 Mississippi 25.95238 15.81429 #### 以下、色々なパターンの集計例 ## 【例1】3変量の集計 > acast(mCO2, Type~Treatment~Plant , sum) Using sv as value column: use value.var to override. , , Qn1 nonchilled chilled Quebec 232.6 0 Mississippi 0.0 0 , , Qn2 nonchilled chilled Quebec 246.1 0 Mississippi 0.0 0 (以下省略) ## 【例2】3変量の集計 本質的に例1と同じだが表示形式が異なる > acast(mCO2, Plant+Type~Treatment , sum) Using sv as value column: use value.var to override. nonchilled chilled Qn1_Quebec 232.6 0.0 Qn2_Quebec 246.1 0.0 Qn3_Quebec 263.3 0.0 Qc1_Quebec 0.0 209.8 Qc3_Quebec 0.0 228.1 Qc2_Quebec 0.0 228.9 Mn3_Mississippi 168.8 0.0 Mn2_Mississippi 191.4 0.0 Mn1_Mississippi 184.8 0.0 Mc2_Mississippi 0.0 85.0 Mc3_Mississippi 0.0 121.1 Mc1_Mississippi 0.0 126.0 ## 【例3】単に計数を集計したい場合は sum や mean を省略すればよい # ちなみに、sumやmeanだけでなくその他の既存関数や自作関数も指定できる > acast(mCO2, Plant+Type~Treatment) Using sv as value column: use value.var to override. Aggregation function missing: defaulting to length nonchilled chilled Qn1_Quebec 7 0 Qn2_Quebec 7 0 Qn3_Quebec 7 0 Qc1_Quebec 0 7 Qc3_Quebec 0 7 Qc2_Quebec 0 7 Mn3_Mississippi 7 0 Mn2_Mississippi 7 0 Mn1_Mississippi 7 0 Mc2_Mississippi 0 7 Mc3_Mississippi 0 7 Mc1_Mississippi 0 7
さて、上記で melt() という関数を使ってデータフレームを「違う形式」に変換していたのに気付いただろうか。実はこれが acast() を使って集計するための、データの保持形式を変えるための「おまじない」だったのです。最後に、この melt() の補足説明をしておきます。
#### ■先ほど書いたコードをそのまま再掲 ## 「おまじない」のような前処理と思えばよい > mCO2 <- melt(CO2, # データフレーム id.vars=c("Plant", "Type", "Treatment"), # グループ集計のキーになる変数 measure.vars="uptake", # 実際に集計する「値」の変数 variable.name="sn", # テキトーな名前でOK value.name="sv") # これもテキトーでOK ## 【例1】id.varsを省略すると、measure.varsで指定された以外の全ての変数がid.varsとなる > mCO2 <- melt(CO2, # データフレーム measure.vars=c("conc", "uptake"), # 実際に集計する「値」の変数 variable.name="sn", # テキトーな名前でOK value.name="sv") # これもテキトーでOK ## 【例2】逆にmeasure.varsを省略すると、id.varsで指定された以外の全ての変数がmeasure.varsになる # ※例1と例2は同じ結果になる mCO2 <- melt(CO2, # データフレーム id.vars=c("Plant", "Type", "Treatment"), # グループ集計のキーになる変数 variable.name="sn", # テキトーな名前でOK value.name="sv") # これもテキトーでOK ## 例1、例2どちらでもよいが中身を見てみると以下のようなデータができている # snは集計対象となる「値」のラベルであり、svが集計対象となる「値」そのものとなっている > mCO2 Plant Type Treatment sn sv 1 Qn1 Quebec nonchilled conc 95.0 2 Qn1 Quebec nonchilled conc 175.0 3 Qn1 Quebec nonchilled conc 250.0 4 Qn1 Quebec nonchilled conc 350.0 5 Qn1 Quebec nonchilled conc 500.0 (途中省略) 85 Qn1 Quebec nonchilled uptake 16.0 86 Qn1 Quebec nonchilled uptake 30.4 87 Qn1 Quebec nonchilled uptake 34.8 88 Qn1 Quebec nonchilled uptake 37.2 89 Qn1 Quebec nonchilled uptake 35.3 (以下省略) ## 【例3】上記のように集計対象(measure.varsに指定された変数)を複数指定しておくと、 # グループ集計もいっぺんにできて便利 > acast(mCO2, Type~Treatment~sn,mean) # snごとに、Type×Treatmentの平均を集計 Using sv as value column: use value.var to override. , , conc nonchilled chilled Quebec 435 435 Mississippi 435 435 , , uptake nonchilled chilled Quebec 35.33333 31.75238 Mississippi 25.95238 15.81429 ## 【例4】特定の条件にマッチするレコードについて集計する(Excelピボットの「レポートフィル」に該当) # やり方としては以下の2通り: > acast(mCO2, Type~Treatment~sn, mean, subset = .(sn=="conc")) # 例3と同じ。集計後に表示を制御(?) Using sv as value column: use value.var to override. , , conc nonchilled chilled Quebec 435 435 Mississippi 435 435 > acast(mCO2, Type~Treatment, mean, subset = .(sn=="conc")) # 例3と同じ。レコードを絞ってから集計(?) Using sv as value column: use value.var to override. nonchilled chilled Quebec 435 435 Mississippi 435 435 ## 【例5】違うデータセットでの例 ※acastのヘルプに書かれている例 #Chick weight example names(ChickWeight) <- tolower(names(ChickWeight)) chick_m <- melt(ChickWeight, id=2:4, na.rm=TRUE) # 上の例4で示したsubsetには複雑な条件絞りを加えられる > acast(chick_m, chick ~ time, mean, subset = .(time < 10 & chick < 20)) 0 2 4 6 8 18 39 35 NaN NaN NaN 16 41 45 49 51 57 15 41 49 56 64 68 13 41 48 53 60 65 9 42 51 59 68 85 # sumやmeanだけでなくlengthのような関数も指定できる # また、drop=FALSEとすることで「全て0」のレコードも表示させることができる(デフォルトはTRUE) > acast(chick_m, diet + chick ~ time, length, drop=FALSE) # 引数drop=FALSEとする 0 2 4 6 8 10 12 14 16 18 20 21 1_18 1 1 0 0 0 0 0 0 0 0 0 0 1_16 1 1 1 1 1 1 1 0 0 0 0 0 1_15 1 1 1 1 1 1 1 1 0 0 0 0 1_13 1 1 1 1 1 1 1 1 1 1 1 1 1_9 1 1 1 1 1 1 1 1 1 1 1 1 1_20 1 1 1 1 1 1 1 1 1 1 1 1 1_10 1 1 1 1 1 1 1 1 1 1 1 1 1_8 1 1 1 1 1 1 1 1 1 1 1 0 1_17 1 1 1 1 1 1 1 1 1 1 1 1 1_19 1 1 1 1 1 1 1 1 1 1 1 1 1_4 1 1 1 1 1 1 1 1 1 1 1 1 1_6 1 1 1 1 1 1 1 1 1 1 1 1 1_11 1 1 1 1 1 1 1 1 1 1 1 1 1_3 1 1 1 1 1 1 1 1 1 1 1 1 1_1 1 1 1 1 1 1 1 1 1 1 1 1 1_12 1 1 1 1 1 1 1 1 1 1 1 1 1_2 1 1 1 1 1 1 1 1 1 1 1 1 1_5 1 1 1 1 1 1 1 1 1 1 1 1 1_14 1 1 1 1 1 1 1 1 1 1 1 1 1_7 1 1 1 1 1 1 1 1 1 1 1 1 1_24 0 0 0 0 0 0 0 0 0 0 0 0 ←全て0のレコードが表示されている 1_30 0 0 0 0 0 0 0 0 0 0 0 0 (以下省略) > acast(chick_m, diet + chick ~ time, length, drop=TRUE) # 引数drop=TRUEとする(省略時はTRUE) 0 2 4 6 8 10 12 14 16 18 20 21 1_18 1 1 0 0 0 0 0 0 0 0 0 0 1_16 1 1 1 1 1 1 1 0 0 0 0 0 1_15 1 1 1 1 1 1 1 1 0 0 0 0 1_13 1 1 1 1 1 1 1 1 1 1 1 1 1_9 1 1 1 1 1 1 1 1 1 1 1 1 1_20 1 1 1 1 1 1 1 1 1 1 1 1 1_10 1 1 1 1 1 1 1 1 1 1 1 1 1_8 1 1 1 1 1 1 1 1 1 1 1 0 1_17 1 1 1 1 1 1 1 1 1 1 1 1 1_19 1 1 1 1 1 1 1 1 1 1 1 1 1_4 1 1 1 1 1 1 1 1 1 1 1 1 1_6 1 1 1 1 1 1 1 1 1 1 1 1 1_11 1 1 1 1 1 1 1 1 1 1 1 1 1_3 1 1 1 1 1 1 1 1 1 1 1 1 1_1 1 1 1 1 1 1 1 1 1 1 1 1 1_12 1 1 1 1 1 1 1 1 1 1 1 1 1_2 1 1 1 1 1 1 1 1 1 1 1 1 1_5 1 1 1 1 1 1 1 1 1 1 1 1 1_14 1 1 1 1 1 1 1 1 1 1 1 1 1_7 1 1 1 1 1 1 1 1 1 1 1 1 2_24 1 1 1 1 1 1 1 1 1 1 1 1 ←上記とは違い全て0のレコードは非表示 2_30 1 1 1 1 1 1 1 1 1 1 1 1