<< トップページへ

<< 目次へ

最終更新日 : 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