# 因子(factor)
```{r, setup, include=FALSE, echo=FALSE}
knitr::opts_chunk$set(
collapse = TRUE
)
```
**因子**はほぼRにのみ存在する特徴的なクラスです。因子は**カテゴリカル変数**(男性と女性、病気の有無など)を表現するためのもので、統計解析において重要な役割を果たします。ただし、因子の挙動はやや複雑で、文字列のつもりが因子だった、因子だと思っていたら文字列だった、などという場合が多々発生します。
因子を作るときには、`factor`関数を用います。引数には文字列や数値のベクターを取ります。また、`gl`関数を用いても因子を作成することができます。
```{r, filename="因子を作成する"}
x <- c("dog", "cat", "pig", "horse")
factor(x)
gl(2, 8, labels=c("A", "B")) # AとBを8回ずつ繰り返す因子
# 4つのラベルを1回ずつ16個まで繰り返す因子
gl(4, 1, 16, labels=c("cat", "dog", "pig", "rat"))
```
因子と文字列を表示したときの違いは、
- 表示したときにダブルクオーテーションがあるかどうか(あれば文字列、なければ因子)
- 表示したときにLevelsが表示されるか(表示されなければ文字列、表示されれば因子)
の2点です。文字列なのか因子なのかわからないときには、コンソールに表示してみるとよいでしょう。
```{r, filename="因子と文字列の違い"}
x <- rep(x, c(5, 4, 3, 2))
x # これは文字列のベクター(ダブルクオーテーション付き)
fx <- factor(x)
fx # こちらは因子のベクター(Levelsを表示)
```
## 因子のレベル(Levels)
因子には**レベル(Levels)**というアトリビュートが存在します。レベルは、因子の種類と順番を指すアトリビュートです。文字列を因子に変えた場合には、アルファベット順にレベルが付与され、数値を因子に変えた場合には、数値の小さいものからレベルが付与されます。レベルの順序は、**グラフや統計結果の表示の順番**に影響を与えます。
因子のレベルを確認する関数には、`nlevels`関数と`levels`関数の2つが存在します。`nlevels`関数は因子のレベルの数を返す関数です。`levels`関数は因子のレベルをそのレベルの順番にそって返します。
```{r, filename="因子のレベルとその順番"}
nlevels(fx)
levels(fx) # levelsはアルファベット順になる
x2 <- c(4, 3, 2, 1)
fx2 <- factor(x2)
levels(fx2) # levelsは数値順になる
```
## 因子のクラスと型
因子の**クラスはfactor、型はnumeric(integer)**です。つまり、**因子は整数にレベルがラベル付けされているクラス**となります。
```{r, filename="因子のクラス・型"}
class(fx)
mode(fx)
typeof(fx)
# 文字列から作った因子もnumericとなる
fch <- c("dog", "cat")
fch <- factor(fch)
mode(fch)
```
因子はそもそも型が数値ですので、`as.numeric`関数で数値に変換できます。このときの数値はレベルの順番に1、2、3...となります。また、因子を`as.character`関数を用いて文字列に変換することもできます。文字列に変換した場合には、レベルの名前がそのまま出力されます。
また、ベクターには、名前(names)というアトリビュートがあるのですが、因子のレベルはこの名前とは異なります。因子のベクターにも名前は別途つけることができますが、過剰に複雑になるので避けた方が良いでしょう。
```{r, filename="因子から数値・文字列への変換"}
fx
as.numeric(fx) # levelsの順に番号が付く
as.character(fx) # 文字列には直接変換できる
# factorの文字はnamesとしては設定されていない(levelsはnamesではない)
names(fx)
fx1 <- fx
names(fx1) <- rep(c("rat", "mouse", "sheep", "monkey"), c(2, 3, 4, 5))
fx1 # 名前付きベクターの因子
```
因子の各レベルの要素の個数を数える場合には、`table`関数を用います。`table`関数を用いると、各レベルと、そのレベルの要素の数が返ってきます。`table`関数は複数の因子や、因子のリスト、データフレームなどを引数に取ることもできます。同様に、`summary`関数でも要素の数を数えることができます。
```{r, filename="因子の個数を数える:table関数"}
table(fx)
factor1 <- factor(rep(c("cat", "dog"), 10))
factor2 <- factor(rep(c("male", "female"), c(7, 13)))
# table関数は因子のベクターを2つ取ることもできる
table(factor1, factor2)
# summary関数でもレベルの要素の数を得ることができる
summary(fx)
```
## レベルの順序を変更する
レベルの順序は、因子を作成するときに`factor`関数の引数に`levels`を指定することで変更できます。`levels`には因子の要素のベクターで指定します。この`levels`に指定した順番に、レベルの順序が決まります。
```{r, filename="レベルの順序を変更する"}
fx
fx2 <- factor(fx, levels = c("cat", "horse", "pig", "dog"))
levels(fx) # レベル順はcat, dog, horse, pig
levels(fx2) # レベルの順序が上のlevelsの順に変更されている
as.numeric(fx) # 元の順序
as.numeric(fx2) # 変更後の順序
```
```{r, echo=FALSE}
d <- data.frame(
func = c("factor(x, levels)", "levels(x)", "nlevels(x)", "table(x)"),
meaning = c("因子を作成する・レベルの順序を変える", "レベルを表示する", "レベルの数を表示する", "各レベルの要素数を表示する")
)
colnames(d) <- c("関数名", "因子xに適用される演算")
knitr::kable(d, caption="表1:因子に関連する関数")
```
## forcats
Rでは因子のレベル順を変更し、グラフや統計結果の表示順を定める場合があります。特に統計学的検定の計算では、対照群(Control)と処理群(Treatment)が因子の順番によって決まる場合があり、因子の順序が計算上重要となります。
デフォルトのRの関数群でも因子の順序などを編集することはできますが、因子の演算を行う専門のパッケージである`forcats` [@forcats_bib]を用いると、より簡潔に、統一感のある因子の演算を行うことができます。`forcats`も`tidyverse` [@tidyverse_bib]に含まれるパッケージの一つです。
```{r, echo=FALSE}
d <- data.frame(
func = c("fct_count(x)", "fct_match(x, pattern)", "fct_unique(x)", "fct_c(x, y)", "fct_unify(list(x, y))", "fct_relevel(x, levels)", "fct_infreq(x)", "fct_inorder(x)", "fct_rev(x)", "fct_shift(x)", "fct_shuffle(x)", "fct_recode(x, newlevel=\"oldlevel\")", "fct_anon(x)", "fct_collapse(x, newlevel=c(levels))", "fct_lump_min(x, min)", "fct_other(x, keep)"),
meaning = c("各レベルの要素の数を返す", "patternのレベルであればTRUEを返す", "各レベルから要素を1つずつ返す", "因子を結合する", "リスト内の因子間でレベルを追加する", "レベルを付け直す", "レベルの要素の数順にレベルをつけ直す", "前にある要素ほど前のレベルにする", "レベルを逆順にする", "レベルの順を1つずらす", "レベルをランダムに並べ替える", "oldlevelのラベルをnewlevelにつけ直す", "レベル名を匿名化する", "2つ以上のレベルを1つにまとめる", "minで指定した数以上のレベルをotherにする", "keepで指定したレベル以外の因子をotherにする")
)
colnames(d) <- c("関数名", "因子xに適用される演算")
knitr::kable(d, caption="表2:forcatsの関数")
```
### 因子の確認
`fct_count`関数は`table`関数とほぼ同じ関数で、因子の要素数を返しますが、結果をデータフレーム(正確にはtibbleというもの)で返す点が`table`関数とは異なります。
`fct_match`関数は、パターンとしてレベル名を設定し、そのレベルと一致する要素では`TRUE`、一致しない要素には`FALSE`を返します。
`fct_unique`関数は`levels`関数とほぼ同じですが、`levels`関数の返り値が文字列なのに対して、`fct_unique`関数は因子を返す点が異なります。
```{r, filename="forcats 因子を確認する"}
# tidyverseをロードすると、forcatもロードされる
pacman::p_load(tidyverse)
fx
# table関数と同じ変換をデータフレーム(正確にはtibble)を出力として行う
fct_count(fx)
fct_match(fx, "dog") # 因子dogを探す関数
fct_unique(fx) # levelsとほとんど同じ関数(因子を返す、levelsは文字列を返す)
```
### 因子を結合する
因子をつなぐときに用いるのが、`fct_c`関数と`fct_unity`関数です。`fct_c`関数は`c`関数と同じですが、因子のリストを引数に取れるという特徴があります。`fact_unify`関数は引数にリストを取り、お互いにレベルを追加できるところが異なります。
```{r, filename="forcats:因子を結合する"}
fx3 <- factor(rep(c("rat", "mouse", "sheep"), c(3, 4, 5)))
c(fx, fx3)
fct_c(fx, fx3) # レベルが追加される(c関数でつないでもほぼ同じ)
fct_unify(list(fx, fx3)) # 因子のリストにそれぞれレベルを追加する
```
### レベルの操作
因子のレベルを付け直すのが`fct_relevel`関数です。`factor`関数に`levels`引数を取るのとほぼ同じことができます。
`fct_infreq`関数は因子のレベルを因子の個数順(多いものが前、少ないものが後)に、`fct_inorder`関数は因子のベクターで前に出てきたものをより前にする形でレベルを変更するものです。
`fct_rev`関数はレベルを逆順に、`fct_shift`関数は一番前のレベルを一番最後にシフトし、`fct_shuffle`関数はレベルの順序をランダムに入れ替える関数です。
```{r, filename="forcats:レベルを操作する"}
fct_relevel(fx, c("dog", "cat", "pig", "horse")) # factor(fx, levels=c("dog", "cat", "pig", "horse"))と同じ
fct_infreq(fx) # 要素が多いものから順番に並べ替える
fct_inorder(fx) # 要素が前にあるものをレベルの前に変更する
fct_rev(fx) # レベルを逆順にする
fct_shift(fx) # レベルを1つずらす
fct_shuffle(fx) # レベルをランダムに並べ替える
```
### レベル名の変更
`fct_recode`関数は因子名を別名に付け替え、`fct_anon`関数は因子名を匿名化(anonymize)するものです。`fct_collapse`関数は因子の複数のレベルを1つにまとめ、`fct_lump_min`関数は`min`引数で指定した数より個数が少ない因子をすべてotherに変えます。`fct_other`関数は`keep`引数で指定したレベル以外をotherに変えます。
いずれも、データをRに取り込んだ後に、余分な因子を処理したり、匿名化することで個人情報等に対応したり、要素が少ない因子を集約するために用いるものです。
```{r, filename="forcats:レベル名を変更する"}
fct_recode(fx, mouse="dog", rat="cat", monkey="pig", cow="horse") # ラベルを付け替える
fct_anon(fx) # 因子を匿名化(anonymize)する
fct_collapse(fx, dogcat = c("dog", "cat")) # レベルを結合する
fct_lump_min(fx, min=4) # 2つより少ない個数しかない因子をotherに変える
fct_other(fx, keep=c("dog", "cat")) # keep以外のレベルをotherに変える
```