Introduction to forcats

Emily Robinson

forcats パッケージの目標は、因子に関する一般的な問題を解決する便利なツール群を提供することです。因子は、カテゴリデータ、固定された既知の値のセットを持つ変数、およびアルファベット順でない文字ベクトルを表示するときに便利です。もっと詳しく知りたい場合は、R for Data Science の chapter on factors から始めるのが最善です。

Ordering by frequency

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は除外することにします。

avg_mass_eye_color <- starwars %>%
  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 の水準をランダムに並べ替えて、シミュレートしてみましょう。

reshuffled_income <- gss_cat$rincome %>%
  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 で別の水準の後に移動させることもできます。末尾に移動させたい場合は、 afterInf に指定します。

例えば、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"