forcats パッケージの目標は、因子に関する一般的な問題を解決する便利なツール群を提供することです。因子は、カテゴリデータ、固定された既知の値のセットを持つ変数、およびアルファベット順でない文字ベクトルを表示するときに便利です。もっと詳しく知りたい場合は、R for Data Science の chapter on factors から始めるのが最善です。
library(dplyr)
library(ggplot2)
library(forcats)
「スター・ウォーズの登場人物で最も一般的な髪の色は?」という質問に答えてみましょう。まずは棒グラフを作ってみます。
ggplot(starwars, aes(x = hair_color)) +
geom_bar() +
coord_flip()
これでもいいのですが、グラフが件数の順に並んでいたほうが助かります。順序付けされていないカテゴリ変数で、頻度で並べたいような場合です。そのためには、関数 fct_infreq()
を使用します。
ggplot(starwars, aes(x = fct_infreq(hair_color))) +
geom_bar() +
coord_flip()
fct_infreq()
は、件数が最小という訳ではないにもかかわらず、自動的にNAを先頭に置くことに注意してください。
今度は肌の色を見てみましょう。
%>%
starwars count(skin_color, sort = TRUE)
#> # A tibble: 31 × 2
#> skin_color n
#> <chr> <int>
#> 1 fair 17
#> 2 light 11
#> 3 dark 6
#> 4 green 6
#> 5 grey 6
#> 6 pale 5
#> 7 brown 4
#> 8 blue 2
#> 9 blue, grey 2
#> 10 orange 2
#> # … with 21 more rows
31種類の肌の色があることがわかります。プロットを作ると、これは表示するには多すぎますね。 これを上位5色に絞ってみましょう。fct_lump()
を使って、頻度の低い色を一つの要素 “other” に “lump” することができます。引数 n
は、保持する水準数です。
%>%
starwars mutate(skin_color = fct_lump(skin_color, n = 5)) %>%
count(skin_color, sort = TRUE)
#> # A tibble: 6 × 2
#> skin_color n
#> <fct> <int>
#> 1 Other 41
#> 2 fair 17
#> 3 light 11
#> 4 dark 6
#> 5 green 6
#> 6 grey 6
また、代わりに prop
を使うこともできます。これは、少なくとも prop
の割合で出現する水準をすべて保持するものです。例えば、少なくとも10%のキャラクターが持っている肌の色を保持することにしましょう。
%>%
starwars mutate(skin_color = fct_lump(skin_color, prop = .1)) %>%
count(skin_color, sort = TRUE)
#> # A tibble: 3 × 2
#> skin_color n
#> <fct> <int>
#> 1 Other 59
#> 2 fair 17
#> 3 light 11
light と fair だけが残り、他はすべて other となります。
other 以外の呼び方にしたい場合、 other_level
という引数で変更することができます。
%>%
starwars mutate(skin_color = fct_lump(skin_color, prop = .1, other_level = "extra")) %>%
count(skin_color, sort = TRUE)
#> # A tibble: 3 × 2
#> skin_color n
#> <fct> <int>
#> 1 extra 59
#> 2 fair 17
#> 3 light 11
目の色によって平均質量が異なるかどうかを調べるとします。ここでは、最も人気のある6色の瞳の色だけを取り上げ、NA
は除外することにします。
<- starwars %>%
avg_mass_eye_color mutate(eye_color = fct_lump(eye_color, n = 6)) %>%
group_by(eye_color) %>%
summarise(mean_mass = mean(mass, na.rm = TRUE))
avg_mass_eye_color#> # A tibble: 7 × 2
#> eye_color mean_mass
#> <fct> <dbl>
#> 1 black 76.3
#> 2 blue 86.5
#> 3 brown 66.1
#> 4 orange 282.
#> 5 red 81.4
#> 6 yellow 81.1
#> 7 Other 68.4
オレンジ色の目の人は、間違いなく体重が重いようです。もしグラフを作るなら、 mean_mass
で並べ替えるとよいでしょう。fct_reorder()
で、ある変数を別の変数で並べ替えるものです。
%>%
avg_mass_eye_color mutate(eye_color = fct_reorder(eye_color, mean_mass)) %>%
ggplot(aes(x = eye_color, y = mean_mass)) +
geom_col()
別のデータセット、gss_cat
(一般社会調査)を使ってみます。回答者の所得分布はどうなっているのでしょうか?
%>%
gss_cat count(rincome)
#> # A tibble: 16 × 2
#> rincome n
#> <fct> <int>
#> 1 No answer 183
#> 2 Don't know 267
#> 3 Refused 975
#> 4 $25000 or more 7363
#> 5 $20000 - 24999 1283
#> 6 $15000 - 19999 1048
#> 7 $10000 - 14999 1168
#> 8 $8000 to 9999 340
#> 9 $7000 to 7999 188
#> 10 $6000 to 6999 215
#> 11 $5000 to 5999 227
#> 12 $4000 to 4999 226
#> 13 $3000 to 3999 276
#> 14 $1000 to 2999 395
#> 15 Lt $1000 286
#> 16 Not applicable 7043
所得水準が正しい順番になっていることに注目してください。無回答から始まり、高いものから低いものへと順番に並んでいます。棒グラフにすると同じ順番になります。これは偶然の一致ではありません。順序データを扱っていると、順序付き因子ができます。確認するには、base関数 levels()
を使い、順序を表示させます。
levels(gss_cat$rincome)
#> [1] "No answer" "Don't know" "Refused" "$25000 or more"
#> [5] "$20000 - 24999" "$15000 - 19999" "$10000 - 14999" "$8000 to 9999"
#> [9] "$7000 to 7999" "$6000 to 6999" "$5000 to 5999" "$4000 to 4999"
#> [13] "$3000 to 3999" "$1000 to 2999" "Lt $1000" "Not applicable"
しかし、もしあなたの因子が間違った順番で表示されたらどうしますか?fct_shuffle()
で rincome
の水準をランダムに並べ替えて、シミュレートしてみましょう。
<- gss_cat$rincome %>%
reshuffled_income fct_shuffle()
levels(reshuffled_income)
#> [1] "$4000 to 4999" "$25000 or more" "$6000 to 6999" "Don't know"
#> [5] "$7000 to 7999" "$20000 - 24999" "No answer" "$3000 to 3999"
#> [9] "Not applicable" "$10000 - 14999" "Refused" "$1000 to 2999"
#> [13] "$8000 to 9999" "Lt $1000" "$15000 - 19999" "$5000 to 5999"
プロットしてみるとこの順番で表示されるのですが、どうしようもありません。 これを修正して正しい順番に並べるにはどうしたらら良いのでしょうか?
手動で因子レベルの順序を変更する必要がある場合、関数 fct_relevel()
を使用することができます。因子に加えて、水準名の文字ベクトルを与え、それらをどこに移動させたいかを指定します。デフォルトでは先頭に移動しましますが、引数 after
で別の水準の後に移動させることもできます。末尾に移動させたい場合は、 after
を Inf
に指定します。
例えば、Lt $1000
と $1000 to 2999
を先頭に移動させたいとします。このように書きます。
fct_relevel(reshuffled_income, c("Lt $1000", "$1000 to 2999")) %>%
levels()
#> [1] "Lt $1000" "$1000 to 2999" "$4000 to 4999" "$25000 or more"
#> [5] "$6000 to 6999" "Don't know" "$7000 to 7999" "$20000 - 24999"
#> [9] "No answer" "$3000 to 3999" "Not applicable" "$10000 - 14999"
#> [13] "Refused" "$8000 to 9999" "$15000 - 19999" "$5000 to 5999"
2位、3位と移動させる場合はどうでしょう。
fct_relevel(reshuffled_income, c("Lt $1000", "$1000 to 2999"), after = 1) %>%
levels()
#> [1] "$4000 to 4999" "Lt $1000" "$1000 to 2999" "$25000 or more"
#> [5] "$6000 to 6999" "Don't know" "$7000 to 7999" "$20000 - 24999"
#> [9] "No answer" "$3000 to 3999" "Not applicable" "$10000 - 14999"
#> [13] "Refused" "$8000 to 9999" "$15000 - 19999" "$5000 to 5999"