Rのdataframe使ってクロス集計した(修論)
この記事は修論でRでクロス集計したときの備忘録です。クロス集計をやりたい場面は結構あると思うので、卒論書く人とかの役に立てば幸いです。
はじめに
筆者のレベル
- 数学超苦手(だけど3DCGのために行列をちょっと自学しはじめた)。プログラミングの素養はそんな無いけどコンソールとかに抵抗はない(むしろRコマンダーとかGUI使う方がなんかめんどい)。
- 卒論でもRはちょっと触った。ディレクトリの概念とか簡単な関数くらいはわかる。
- 修論のためにRをもっと色々としばいて学び始める。前記事→「Rでデータを綺麗にした(修論編)」
という感じで、相変わらず初心者です。
やりたいこと
- クロス集計をしていい感じの図表を出力したい。
やっぱりExcelは嫌いなのでRでやりたい。
問題
- クロス集計をしてくれる代表的な関数としてprop.table()があるが、dataframeをtableに変換しないと使えない。tidyverseパッケージを中心に運用したいので、dataframeはずっとdataframeのまま運用したい。(変換を二回も挟むときったねえコードになって発狂する)
- いわゆるクロス集計表と呼ばれるものは、そのままだとggplot2やgtはうまく読み込んでくれない。「整然データ」と呼ばれるようなdataframeに変換する必要がある。
【参考】「整然データとは何か」
ggplot2パッケージとgtパッケージはdataframeを使ってなんかいい感じのグラフと表が作れるなんかいい感じのパッケージで、ここには詳しく書きませんがネットにも情報が多いので調べてみてください。
解決
1. janitorパッケージを使う
janitorパッケージというものがあるらしく、prop.table()の代わりにこれのtabyl()関数と adorn_percentages()関数を使えば入力も出力もdataframeで扱えて最高でした。
【参考】「janitorパッケージでtidyな度数分布表やクロス表を作成」
2. gather()関数を使ってクロス集計表を整然データに変換する
クロス集計表に限らず、色々な分析結果をggplot2やgtに読み込ませるためにコードを書いて無理やり分類のための列を付与したり並べ替えたりガチャガチャやったりしてたのですが、gather()関数が使える場面では一発だったりするようです。特にクロス集計表の場合は有効です。
【参考】「tidyr::gather( )とtidyr::spread( )でデータフレームを自在に変形する」
実践
dataframe読み込み
クロス集計ができそうな(カテゴリカルデータが2種類以上ある)Rのサンプルデータとしてwarpbreaksを使います。
#サンプルデータ読み込み df <- warpbreaks
warpbreaksの中身はこんな感じです。
> head(df) breaks wool tension 1 26 A L 2 30 A L 3 54 A L 4 25 A L 5 70 A L 6 52 A L > str(df) 'data.frame': 54 obs. of 3 variables: $ breaks : num 26 30 54 25 70 52 51 26 67 18 ... $ wool : Factor w/ 2 levels "A","B": 1 1 1 1 1 1 1 1 1 1 ... $ tension: Factor w/ 3 levels "L","M","H": 1 1 1 1 1 1 1 1 1 2 ...
woolとtensionがFactor型の属性列なのでとりあえずクロス集計ができそうですね。それぞれ水準はwoolはAとB、tensionはLとMとHがあるみたいです。
クロス集計するぞ!
さっそくjanitorパッケージを使ってみます。woolとtensionのクロス集計をしてみましょう!tabyl()という関数を使います。
(説明のため、一旦演算子%>%は使わずに書いていきます。途中経過を保存できるメリットもあるしね!)
library(janitor) #janitorパッケージ読み込み cross <- tabyl(df, wool, tension) #クロス集計表を作る
【実行結果】
> cross wool L M H A 9 9 9 B 9 9 9
できたあああ!!!しかもちゃんとdataframe型です!
(綺麗すぎてびびりますが、たぶんこれ実験結果の属性じゃなくて実験計画の段階でこうなるように操作されているっぽいです。そういう場面で確認のためにクロス集計を使う場面もあるだろうしまあいいでしょう。できたことが大事!)
割合も出してみましょう。adorn_percentages()関数を使います。
cross_rate <- adorn_percentages(cross, "row") #行(横)割合に換算
(今回は行(横)割合を出していますが、"row"を"col"に変えると列(縦)割合になります。
【実行結果】
> cross_rate wool L M H A 0.3333333 0.3333333 0.3333333 B 0.3333333 0.3333333 0.3333333
出せました。これは合計が1になる割合表示なので、%表示にしたいと思います。
……ここで、愚かな私はそれをmutate_at()と100倍するだけの自作関数を組み合わせて無理やり(?)実装してしまったのですが、ちゃんとドキュメントを見たら…そのための関数も…ありました……adorn_pct_formatting()関数を使います! 英語でもめんどくさがらずにドキュメントは読もう!
cross_rate_P <- adorn_pct_formatting(cross_rate, digits = 2) #%に換算
digits=のとこの数字を変えると小数点以下どこまでで丸めるかを指定できます。
【実行結果】
> cross_rate_P wool L M H A 33.33% 33.33% 33.33% B 33.33% 33.33% 33.33%
ご丁寧に単位までつけてくれました。あれこれ文字混入してる(実際型もchrになってる)けど大丈夫なんか?と思ったけどこのままでもggplot2につっこんでも大丈夫そうだったので、なんか大丈夫な内部構造になってるんでしょう!Rなんもわからん!
それが気持ち悪い人は以下の私が作った愚かなコードを使ってください。
library(tidyverse) #tidyverseパッケージ読み込み #100倍するだけの関数を作成 caP <- function(x){ P <- x*100 return(P)} cross_rate_P <- mutate_at(cross_rate, vars(-wool), caP) #%に換算
xを100倍するだけの関数…かわいいね…。(mutate_atのfunctionのところに直で*100とか入れても動かなかったのです。これもなんかいい方法がありそう…Rなんもわからん…)
整然データに直して図表にするぞ!
クロス集計表が欲しいだけの人はここまでで終わっても大丈夫です。ここからは、それを元に表とかグラフとかを作っていきます。
tidyverseパッケージ内のgather()関数をつかってクロス集計表を整然データのdataframeに変換します。
library(tidyverse) #tidyverseパッケージ読み込み tidy_df <- gather(cross_rate_P, key=tension, value=percent, -wool) #整然データに
【実行結果】
> tidy_df wool tension percent 1 A L 33.33% 2 B L 33.33% 3 A M 33.33% 4 B M 33.33% 5 A H 33.33% 6 B H 33.33%
記事を見ても直感的に使い方を理解するのに時間が掛かってしまい、説明し直すのもちょっと難しいのですが、たぶん以下のような感じです。
記事の説明に乗っかると、wool列以外のL,M,H列を”ラベル”にしてtensionという名前を付け直し、wool列以外に格納されている値をvalue列(percentと命名)に並べる、という感じでしょうか。
とにかく、これで整然データになりましたので、gtとかggplotに読み込ませていきます。
library(gt) #gtパッケージ読み込み Table <- gt( tidy_df, rowname_col = "tension", groupname_col = "wool" ) Table
やったぜ。
#ggplot2でグラフ作成 Col <- ggplot(tidy_df, aes(x=wool, y=percent, fill=tension)) + geom_col(width = 0.5) Col
(ggplot2パッケージはtidyverseパッケージの中に入ってるので読み込みなおす必要は今回は無いです)
やったぜ。
gtやggplotに読み込ませられたので、あとは煮るなり焼くなりしてなんかいい感じの図表を作りましょう。その方法についてはここでは触れないので、インターネットに探しに行ってください。
これで何でもかんでもクロス集計することができますね。ではよきデ―タ弄りライフを……私も修論まだ一文字も書いてないので、こんなブログ書いてないで本腰入れましょうね。それでは。
Algo_Ayugon