こんにちは、usagi-san です。
今回は集計表及び、ピボットテーブルの作成方法をみていきます。
クロス集計表は、factor型のデータの各levelの度数、和、平均を取り扱う際に便利です。
エクセルではピボットテーブルという機能を用いれば、直感的に簡単に集計表を作成することが可能です。
エクセルで作成した集計表を用いても解析が可能ですが、R言語でもパッケージを用いればピボットテーブルの作成ができます。
今回はピボットテーブルの作成の例として、2元及び3元のテーブルを作成していきます(2元及び3元分割表を作成していきます)。
以下のダウンロードリンクから、今回用いるプログラミンコードをダウンロードできます。
R言語 クロス集計表・ピボットテーブル Rスクリプト
是非是非ダウンロードしてください
また、データフレームの操作をすべてまとめた記事も書きました。データフレームの詳細については次の記事を参照。
【R言語】データフレーム操作 作成・値の代入・行と列の追加と削除など
R言語を使う際に避けては通れない道であるデータフレームの操作について解説します。 データフレーム(data.frame)はデータセットを格納するための行列であり、データ解析をするうえで必要不可欠です。 ...
続きを見る
クロス集計表の作り方
データセットの準備
まずは、クロス集計表(2元の分割表)を作る方法を解説していきます。
R言語にデフォルトで入っているmtcarsという自動車のテストデータセットを例に集計方法を解説していきます。
まず次のコードを実行し、dfにmtcartを代入しましょう。
1 | df <- mtcars #自動車のテストのデータセット |
試しに、dfの列名および、データの構造を確認してみましょう。関数headを用いるとデータフレームの一部を参照することができます。
1 2 3 4 5 6 7 8 | > head(df) mpg cyl disp hp drat wt qsec vs am gear carb Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 |
上の実行結果のように、dfはmpgなどのnumeric型の列とcyl、vs、am、gear、carbといったfactor型の列から成ることが分かります。
factor型のデータを例にいろいろな集計方法をみていきます。
クロス集計表の作成
では、vsとamに関するクロス集計を行います。
df$vsとdf$amのように各2つの列名(タグ)を指定し、次のようにas.factorやlevelsを用いることで、0と1のlevelをもつfactro型のデータであることが分かります(注意: vsとamなどの列のデータはデフォルトだとcharacter型です)。
1 2 3 4 5 6 7 | > as.factor(df$vs) [1] 0 0 1 1 0 1 0 1 1 1 1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 1 0 1 0 0 0 1 Levels: 0 1 > > as.factor(df$am) [1] 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 Levels: 0 1 |
これら2つの列のクロス集計表を作成する際に、次の表のように4通りの組み合わせを考える必要があります。
vs\am | 0 | 1 |
0 | vs=0かつam=0 | vs=0かつam=1 |
1 | vs=1かつam=0 | vs=1かつam=1 |
度数についてのクロス集計
まず、上の表にある条件式を用いて各セルの度数を計算してみます。
次のようにして、上の表の4通りのパターンを計算することができます。
1 2 3 4 5 | #1. vsとamに関する度数 table_11 <- nrow(df[df$vs == 0 & df$am == 0, ]) #vs=0かつam=0 table_12 <- nrow(df[df$vs == 0 & df$am == 1, ]) #vs=0かつam=1 table_21 <- nrow(df[df$vs == 1 & df$am == 0, ]) #vs=1かつam=0 table_22 <- nrow(df[df$vs == 1 & df$am == 1, ]) #vs=1かつam=1 |
table_11には、vs=0かつam=0の度数が代入されているように、tableの添え字には、線形代数のブロック行列に対応するものとなっています(11の場合、左上の成分。12の場合、右上の成分, . . . )。
次のコードを実行することで、table_11, . . . table_22の各成分を用いてクロス集計表を作成することができます。
1 2 3 | table <- data.frame(c(table_11, table_21), c(table_12, table_22)) rownames(table) <- c(0, 1) colnames(table) <- c(0, 1) |
また、rownames(table)とcolnames(table)をそれぞれc(0, 1)にし、集計表の行名と列名を指定しています。
クロス集計表tableをコンソール上で確認すると、次のようにvsとamの集計表を作成できたことが分かります。
1 2 3 4 | > table #2×2分割表 0 1 0 12 6 1 7 7 |
和についてのクロス集計
次に、列名mpgの各セルの和についてのクロス主計表を作成していきます。
R言語 データフレーム練習【初心者向け】で説明したように、関数sumを用いることで、numeric型のデータの和を計算することができます。
度数の場合と同様に、各4パターンのdf$mpgを考え、関数sumを用いて、それぞれのnumeric型のベクトルの和を求めています。
1 2 3 4 5 | #2. vsとamに関するmpgの和 sumTable_11 <- sum(df$mpg[df$vs == 0 & df$am == 0]) #vs=0かつam=0 sumTable_12 <- sum(df$mpg[df$vs == 0 & df$am == 1]) #vs=0かつam=1 sumTable_21 <- sum(df$mpg[df$vs == 1 & df$am == 0]) #vs=1かつam=0 sumTable_22 <- sum(df$mpg[df$vs == 1 & df$am == 1]) #vs=1かつam=1 |
例として、vs=0かつam=0のとき、関数sumの引数df$mpg[df$vs == 0 & df$am == 0]は次のようになっています。
1 2 3 4 | > df$mpg[df$vs == 0 & df$am == 0] [1] 18.7 14.3 16.4 17.3 15.2 10.4 10.4 14.7 15.5 15.2 13.3 19.2 > length(df$mpg[df$vs == 0 & df$am == 0]) [1] 12 |
2行目では、vs=0かつam=0である行のdf$mpgが 出力されていることが確認できます。
またlengthが12であることは、先ほど計算したvs=0かつam=0の度数と一致しているのもわかります。
度数の場合と同様に、次を実行することで、sumTable_11、sumTable_12、sumTable_21、sumTable_22をクロス集計表にまとめます。
1 2 3 | sumTable <- data.frame(c(sumTable_11, sumTable_21), c(sumTable_12, sumTable_22)) rownames(sumTable) <- c(0, 1) colnames(sumTable) <- c(0, 1) |
コンソール上で、sumTableを確認すると、和に関するクロス集計表ができていることが確認できます。
1 2 3 4 | > sumTable #和の2×2分割表 0 1 0 180.6 118.5 1 145.2 198.6 |
平均についてのクロス集計
最後に、列名mpgの各セルの平均についてのクロス主計表を作成していきます。
各セルの平均は、度数の集計表と和の集計表から計算することができます。
ベクトルや行列、データフレームは各要素についての四則演算が可能であるように、次の割り算/で平均についてのクロス集計表を求めることができます。
1 2 | #3. csとamに関するmpgの平均 meanTable <- sumTable / table #和の各要素を度数の各要素で割る |
コンソール上で、meanTableを参照すると、次の平均についてのクロス集計表ができていることが分かります。
1 2 3 4 | > meanTable #平均の2×2分割表 0 1 0 15.05000 19.75000 1 20.74286 28.37143 |
2元の場合のクロス集計の方法を説明しましたが、実行するとわかるように、factor型のlevelの数が多くなればなるほど、作成が困難になります。
そのため次では、集計用の関数xtabsを用いた集計表の作成の一連の流れを説明します。
xtabsを用いた作成
クロス集計用の関数として、xtabsを紹介します。
先ほどのクロス集計表の作成の例では、各levelsのパターンを考えて1から作成しましたが、xtabsを用いることで、この計算の手間を省くことができます。
xtabsを用いた度数のクロス集計
xtabsを用いて度数についてのクロス集計表を作成するには、xtabsの引数を次のように与えます。
1 2 | #1. vsとamに関する度数 table <- xtabs(~ vs + am, data = df) |
1つ目の引数には式を代入します。今、vsとamの集計表を得たいので"~ vs + am"としています。
2つ目に、集計するデータフレームを代入しています。
tableを参照すると、tableには次のようなint型のリストが格納されているのが分かります。
1 2 3 4 5 | > table am vs 0 1 0 12 6 1 7 7 |
xtabsを用いた和のクロス集計
xtabsを用いて和の集計を行うには、xtabsの最初の引数である式の"~"の前に和を求める列名を指定します。
次のようにxtabsの引数を指定することで、df$mpgの和についての集計表を作成することができます。
1 2 | #2. vsとamに関するmpgの和 sumTable <- xtabs(mpg ~ vs + am, data = df) |
度数の場合と同様に、sumtableには次のようなint型のリストが格納されているのが分かります。
1 2 3 4 5 | > sumTable am vs 0 1 0 180.6 118.5 1 145.2 198.6 |
xtabsを用いた平均のクロス集計
xtabsで出力される度数と和の集計表を用いて、平均のクロス集計表を作成することができます。
次のように、割り算/により平均についてのクロス集計表を作成できます。
1 2 | #3. csとamに関するmpgの平均 meanTable <- sumTable / table |
meanTableには、平均に関するint型のリストが格納されていることがわかります。
1 2 3 4 5 | > meanTable am vs 0 1 0 15.05000 19.75000 1 20.74286 28.37143 |
csvファイルへの出力
xtabsで作成した集計表は、参照するとわかるようにlint型のリストとなっており、集計表の形でのcsvファイルへの出力は難しいです。
行名や列名を結合しなおすことで、集計表の形を維持したままcsvファイルに出力することができます。
まず扱いやすくするために、int型のリストをmatrixに変換します。
1 2 3 | matrixTable <- as.matrix(table) #扱いやすよう行列にする matrixSumTable <- as.matrix(sumTable) matrixMeanTable <- as.matrix(meanTable) |
次のrbindとcbindで、クロス集計表の行名と列名およびvsとamを結合させています。
1 2 3 4 5 6 | table <- cbind(c("", "", "vs", ""), c("", "", rownames(matrixTable)), rbind(c("am", ""), colnames(matrixTable), matrixTable)) sumTable <- cbind(c("", "", "vs", ""), c("", "", rownames(matrixSumTable)), rbind(c("am", ""), colnames(matrixSumTable), matrixSumTable)) meanTable <- cbind(c("", "", "vs", ""), c("", "", rownames(matrixMeanTable)), rbind(c("am", ""), colnames(matrixMeanTable), matrixMeanTable)) |
例として、tableを参照すると、集計表の形をしたデータフレームが作成されていることが確認できます。
1 2 3 4 5 6 | > table 0 1 "" "" "am" "" "" "" "0" "1" 0 "vs" "0" "12" "6" 1 "" "1" "7" "7" |
最後に、csvファイルへの出力には、write.csvではなく、write.tableを用います。
これは、write.csvでは引数col.names = TRUEなどの変更が許されないためです。今、上の実行結果から、tableにはすでに列名が挿入されているのが分かるのでcol.names = Fとしてcsvファイルに出力していきます。
次のようにwrite.tableの引数にrow.names = F や col.names = Fを指定することで、上の出力結果の集計表をcsvファイルを出力することができます。
1 2 3 | write.table(table, "分割表(度数).csv", quote = F, sep = ",", row.names = F, col.names = F) write.table(sumTable, "分割表(和).csv", quote = F, sep = ",", row.names = F, col.names = F) write.table(meanTable, "分割表(平均).csv", quote = F, sep = ",", row.names = F, col.names = F) |
次の画像のように、分割表(度数).csvをみてみると、xtabsで出力した集計表が保存されていることが分かります。
3元以上のクロス集計表
3元以上のクロス集計表の作成
3元以上のクロス集計表を解説していきます。
dfのvsとamとgearを例にクロス集計表を作成してみます。
as.factor(df$gear)を参照してみます。
1 2 3 | > as.factor(df$gear) [1] 4 4 4 3 3 3 3 4 4 4 4 3 3 3 3 3 3 4 4 4 3 3 3 3 3 4 5 5 5 5 5 4 Levels: 3 4 5 |
df$gearは上の3つのlevelsをもつfactor型のデータであることと、クロス集計表の作成からdfのvsとamとgearのクロス集計表は次の12パターンのセルから成る表であることが分かります。
gear | 3 | 4 | 5 | |
vs | am | |||
0 | 0 | vs=0かつam=0かつgear=3 | vs=0かつam=0かつgear=4 | vs=0かつam=0かつgear=5 |
1 | vs=0かつam=1かつgear=3 | vs=0かつam=1かつgear=4 | vs=0かつam=1かつgear=5 | |
1 | 0 | vs=1かつam=0かつgear=3 | vs=1かつam=0かつgear=4 | vs=1かつam=0かつgear=5 |
1 | vs=1かつam=1かつgear=3 | vs=1かつam=1かつgear=4 | vs=1かつam=1かつgear=5 |
クロス集計(3元)
2元の場合と同様に、まずxtabsを用いないでクロス集計表を作ってみます。
上の表にある条件式を用いて各セルの度数を計算してみます。
次のようにして、上の表の12通りのパターンを計算することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #1. vsとamとgearに関する度数 tableList <- NULL #分割表のリストを初期化 sumTableList <- NULL meanTableList <- NULL for(i in 1:nlevels(as.factor(df$gear))){ gearLevels <- levels(as.factor(df$gear)) table_11 <- nrow(df[df$vs == 0 & df$am == 0 & df$gear == gearLevels[i], ]) #vs=0かつam=0かつgear=gearLevels[i] table_12 <- nrow(df[df$vs == 0 & df$am == 1 & df$gear == gearLevels[i], ]) #vs=0かつam=1かつgear=gearLevels[i] table_21 <- nrow(df[df$vs == 1 & df$am == 0 & df$gear == gearLevels[i], ]) #vs=1かつam=0かつgear=gearLevels[i] table_22 <- nrow(df[df$vs == 1 & df$am == 1 & df$gear == gearLevels[i], ]) #vs=1かつam=1かつgear=gearLevels[i] sumTable_11 <- sum(df$mpg[df$vs == 0 & df$am == 0 & df$gear == levels(df$gear)[i]]) #vs=0かつam=0かつgear=gearLevels[i] sumTable_12 <- sum(df$mpg[df$vs == 0 & df$am == 1 & df$gear == levels(df$gear)[i]]) #vs=0かつam=1かつgear=gearLevels[i] sumTable_21 <- sum(df$mpg[df$vs == 1 & df$am == 0 & df$gear == levels(df$gear)[i]]) #vs=1かつam=0かつgear=gearLevels[i] sumTable_22 <- sum(df$mpg[df$vs == 1 & df$am == 1 & df$gear == levels(df$gear)[i]]) #vs=1かつam=1かつgear=gearLevels[i] table <- data.frame(c(table_11, table_21), c(table_12, table_22)) sumTable <- data.frame(c(sumTable_11, sumTable_21), c( |