-
Notifications
You must be signed in to change notification settings - Fork 6
/
factor.qmd
769 lines (572 loc) · 37.2 KB
/
factor.qmd
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
# データハンドリング [factor型] {#sec-factor}
```{r}
#| include: false
source("_common.R")
pacman::p_load(tidyverse, gt, gtExtras)
```
## 名目変数を含むグラフを作成する際の注意点
ここからは楽しい可視化、つまりグラフの作成について解説します。ただし、その前に、名目変数の扱いと簡潔データ構造について話したいと思います。本章では名目変数の扱いについて解説し、次章は簡潔データ構造について解説します。
横軸、または縦軸が気温、成績、身長のような連続変数ではなく、都道府県や国、企業のような名目変数になる場合があります。たとえば、棒グラフの横軸は @fig-factor1 のように、一般的に名目変数になる場合が多いです。
```{r}
#| label: fig-factor1
#| fig-width: 6
#| fig-height: 4
#| message: false
#| echo: false
#| fig-cap: "横軸が名目変数の棒グラフ"
library(tidyverse)
df <- read_csv("Data/Ramen.csv")
df |>
group_by(Pref) |>
summarise(Score = mean(Score, na.rm = TRUE),
.groups = "drop") |>
arrange(Score) |>
ggplot() +
geom_bar(aes(x = Pref, y = Score), stat = "identity") +
coord_cartesian(ylim = c(1, 5)) +
labs(x = "都府県", y = "口コミ評価の平均値 (1~5)") +
theme_gray() +
theme(text = element_text(size = 12))
```
ここでは横軸の順番に注目してください。京都府、埼玉県、神奈川県、...の順番になっていますね。「この順番で大満足だよ!」という方がいるかも知れませんが、そうでない方もおおいでしょう。普通考えられるものとしては、都道府県コードの順か、縦軸が高い順 (低い順)でしょう。都道府県コードの順だと、埼玉県、千葉県、東京都、神奈川県、京都府、大阪府、兵庫県、奈良県、和歌山県の順番になります。または、縦軸 (口コミ評価の平均値)が高い順なら和歌山県、奈良県、大阪府、...の順番になります。あるいは50音順も考えられるでしょう。アメリカの場合、州を並べる際、アルファベット順で並べます。
自分でこの順番をコントロールするには可視化の前の段階、つまりデータハンドリングの段階で順番を決めなくてはなりません。これを決めておかない場合、Rが勝手に順番を指定します。具体的にはロケール (locale)というパソコン内の空間に文字情報が含まれているわけですが、そこに保存されている文字の順番となります。たとえば、日本語ロケールには「京」が「埼」よりも先に保存されているわけです。
したがって、名目変数がグラフに含まれる場合は、名目変数の表示順番を決める必要があり、そこで必要なのがfactor型です。名目変数がcharacter型の場合、ロケールに保存されている順でソートされますが、factor型の場合、予め指定した順番でソートされます。
たとえば、[前章で使用したデータ](Data/Ramen.csv)を用いて、都道府県ごとの口コミ評価の平均値を計算し、その結果を`Score_df`として保存します。
```{r}
#| eval: false
# tidyverseパッケージの読み込み
library(tidyverse)
# データの読み込み
df <- read_csv("Data/Ramen.csv")
```
```{r}
Score_df <- df |>
group_by(Pref) |>
summarise(Score = mean(Score, na.rm = TRUE),
.groups = "drop")
Score_df
```
この時点で勝手にロケール順になります。実際、表示された`Score_df`を見ると`Pref`の下に`<chr>`と表記されており[^tibble-datatype]、`Pref`はcharacter型であることが分かります。これをこのまま棒グラフに出してみましょう。可視化の方法はこれから詳細に解説するので、ここでは結果だけに注目してください。
[^tibble-datatype]: tibble型でなく、data.frame型の場合、各列のデータ型は表示されませんので、csv形式の読み込みの際は、`read_csv()`を使いましょう。
```{r}
#| label: fig-factor2
#| fig-width: 6
#| fig-height: 4
#| fig-cap: "Prefがcharacter型の場合 (1)"
Score_df |>
ggplot() +
geom_bar(aes(x = Pref, y = Score), stat = "identity") +
labs(x = "都府県", y = "口コミ評価の平均値 (1~5)") +
theme(text = element_text(size = 12))
```
横軸の順番があまり直感的ではありませんね。それでは、`Score_df`を`Score`が高い順にソートし、`Score_df2`で保存してから、もう一回試してみます。
```{r}
Score_df2 <- Score_df |>
arrange(desc(Score))
Score_df2
```
ここでも`Pref`はcharacter型ですが、とりあえず、これで図を出してみます。
```{r}
#| label: fig-factor3
#| fig-width: 6
#| fig-height: 4
#| fig-cap: "Prefがcharacter型の場合 (2)"
Score_df2 |>
ggplot() +
geom_bar(aes(x = Pref, y = Score), stat = "identity") +
labs(x = "都府県", y = "口コミ評価の平均値 (1~5)") +
theme(text = element_text(size = 12))
```
結果は全く変わっておりません。それでは、`Score_df`の`Pref`列をfactor型に変換し、順番は口コミ評価の平均値が高い順番にしてみましょう。結果は`Score_df_f1`という名で保存します。
```{r}
Score_df_f1 <- Score_df |>
mutate(Pref = factor(Pref, levels = c("和歌山県", "奈良県", "大阪府",
"千葉県", "京都府", "東京都",
"埼玉県", "兵庫県", "神奈川県")))
Score_df_f1
```
表示される順番は`Score_df`と`Score_df_f1`も同じですが、`Pref`のデータ型が`<fct>`、つまりfactor型であることが分かります。実際、`Pref`列だけ抽出した場合、factor型として、和歌山県から神奈川県の順になっていることが確認できます。
```{r}
Score_df_f1$Pref
```
この`Score_df_f1`データを使って、 @fig-factor2 と全く同じコードを実行した結果が @fig-factor4 です。
```{r}
#| label: fig-factor4
#| fig-width: 6
#| fig-height: 4
#| fig-cap: "Prefがcharacter型の場合 (3)"
Score_df_f1 |>
ggplot() +
geom_bar(aes(x = Pref, y = Score), stat = "identity") +
labs(x = "都府県", y = "口コミ評価の平均値 (1~5)") +
theme(text = element_text(size = 12))
```
これまでの話をまとめるの以下の2点が分かります。
1. 変数がcharacter型である場合、自動的にロケール順でソートされる。
2. 変数がfactor型である場合、データ内の順番やロケール順と関係なく、指定されたレベル (水準)の順でソートされる。
とくに2番目の点についてですが、これは必ずしも順序付きfactorである必要はありません。順序付きfactor型でなくても、`factor()`内で指定した順にソートされます。むろん、順序付きfactor型なら指定された順序でソートされます。
これからはfactor型変換の際に便利な関数をいくつか紹介しますが、その前に数値として表現された名目変数について話します。たとえば、`Score_df_f1`に関東地域なら`1`を、その他の地域なら`0`を付けた`Kanto`という変数があるとします。
```{r}
Score_df_f1 <- Score_df_f1 |>
mutate(Kanto = ifelse(Pref %in% c("東京都", "神奈川県", "千葉県", "埼玉県"), 1, 0))
Score_df_f1
```
`Kanto`変数のデータ型は、`<dbl>`、つまりnumeric型です。しかし、これは明らかに名目変数ですね。これをこのまま`Kanto`を横軸にした図を出すと @fig-factor5 のようになります。
```{r}
#| label: fig-factor5
#| fig-width: 6
#| fig-height: 4
#| echo: false
#| fig-cap: "Kantoがnumeric型の場合"
Score_df_f1 |>
ggplot() +
geom_bar(aes(x = Kanto, y = Score), stat = "identity") +
labs(x = "関東地域", y = "口コミ評価の平均値 (1~5)") +
theme(text = element_text(size = 12))
```
この場合、図の横軸は`Kanto`の値が小さい順でソートされます。ただし、このような図は非常に見にくいため、`1`に`"関東"`、`0`に`"関西"`とラベルを付けたfactor型に変換した方が望ましいです。numeric型をラベル付きのfactor型にするためには、`levels`引数には元の数値を、`labels`引数にはそれぞれの数値に対応したラベルを指定します。また、関東の方を先に出したいので、`factor()`内の`levels`引数は`c(0, 1)`でなく、`c(1, 0)`にします。
```{r}
Score_df_f1 <- Score_df_f1 |>
mutate(Kanto = factor(Kanto, levels = c(1, 0), labels = c("関東", "その他")))
Score_df_f1
```
`Kanto`変数がfactor型に変換されたことが分かります。
```{r}
Score_df_f1$Kanto
```
また、`"関東"`、`"その他"`の順になっていますね。これを図として出力した結果が @fig-factor6 です。
```{r}
#| label: fig-factor6
#| fig-width: 6
#| fig-height: 4
#| echo: false
#| fig-cap: "Kantoがfactor型の場合"
Score_df_f1 |>
ggplot() +
geom_bar(aes(x = Kanto, y = Score), stat = "identity") +
labs(x = "地域", y = "口コミ評価の平均値 (1~5)") +
theme(text = element_text(size = 12))
```
このように数値型名目変数でも、factor化することによって、自由に横軸の順番を変えることができます。それでは、factor化に使える便利な関数をいくつか紹介します。
## {forcats}パッケージについて
実はfactor型への変換や、順番に変更などは全てR内蔵の`factor()`関数で対応可能ですが、ここでは{forcats}パッケージが提供している`fct_*()`関数を使用します。{forcats}パッケージは{tidyverse}を読み込む際、自動的に読み込まれるため、既に{tidyverse}を読み込んでいる場合、別途のコードは要りません。
### `fct_relevel()`: 水準の順番を変更する
`Score_df_f1`の`f1`は`Score`が高い順になっています。これを50音順に変更する際、`fct_relevel()`関数を使います。
```{r}
#| eval: false
# 新しい変数名と元となる変数名が一致すると上書きになる
データフレーム名 |>
mutate(新しい変数名 = fct_releve(元となる変数名,
"水準1", "水準2", "水準3", ...))
```
ここでは、`Pref`変数を再調整した`Pref2`変数を作ってみましょう。
```{r}
Score_df_f1 <- Score_df_f1 |>
mutate(Pref2 = fct_relevel(Pref, "大阪府", "神奈川県", "京都府",
"埼玉県", "千葉県", "東京都",
"奈良県", "兵庫県", "和歌山県"))
Score_df_f1
```
一見、`Pref`と`Pref2`変数は同じように見えますが、水準はどうなっているでしょうか。
```{r}
levels(Score_df_f1$Pref) # Prefの水準
levels(Score_df_f1$Pref2) # Pref2の水準
```
問題なく50音順になっていることが分かります。他にも`fct_relevel()`には全ての水準名を指定する必要がありません。一部の水準名も可能です。たとえば、「関東が関西の先に来るなんでけしからん!」と思う読者もいるでしょう。この場合、関西の府県名を入れると、指定した水準が最初に位置するようになります。
```{r}
Score_df_f1 <- Score_df_f1 |>
mutate(Pref3 = fct_relevel(Pref, "京都府", "大阪府",
"兵庫県", "奈良県", "和歌山県"))
levels(Score_df_f1$Pref3) # Pref3の水準
```
一部の水準名のみを指定するとその水準が最初に移動されますが、`after`引数を指定すると、位置を調整することも可能です。`after = 2`の場合、元となる変数の1、3番目の水準は維持され、3番目以降に指定した水準、それに続いて指定されていない水準の順番になります。`Pref`は和歌山、奈良、大阪の順ですが、ここで京都と東京を、奈良と大阪の間に移動するなら、
```{r}
Score_df_f1 <- Score_df_f1 |>
mutate(Pref4 = fct_relevel(Pref, "京都府", "東京都", after = 2))
levels(Score_df_f1$Pref4) # Pref4の水準
```
のように書きます。`after`を指定しない場合のデフォルト値は0であるため、最初に移動します。
### `fct_recode()`: 水準のラベルを変更する
`fct_recode()`は水準のラベルを変更する時に使う関数で、以下のように使います。
```{r}
#| eval: false
# 新しい変数名と元となる変数名が一致すると上書きになる
データフレーム名 |>
mutate(新しい変数名 = fct_recode(元となる変数名,
新しいラベル1 = "既存のラベル1",
新しいラベル2 = "既存のラベル2",
新しいラベル3 = "既存のラベル3",
...))
```
注意点としては新しいラベルは`"`で囲まず、既存のラベルは`"`で囲む点です。それでは、`Pref`のラベルをローマ字に変更してみましょう。
```{r}
Score_df_f1 <- Score_df_f1 |>
mutate(Pref5 = fct_recode(Pref,
Saitama = "埼玉県",
Wakayama = "和歌山県",
Kyoto = "京都府",
Osaka = "大阪府",
Tokyo = "東京都",
Nara = "奈良県",
Kanagawa = "神奈川県",
Hyogo = "兵庫県",
Chiba = "千葉県"))
Score_df_f1
```
`fct_recode()`の中に指定する水準の順番は無視されます。つまり、水準の順番はそのまま維持されるため、好きな順番で結構です。また、全ての水準を指定せず、一部のみ変更することも可能です。それでは`Pref5`の順番が`Pref`の順番と同じかを確認してみましょう。
```{r}
levels(Score_df_f1$Pref) # Prefの水準
levels(Score_df_f1$Pref5) # Pref5の水準
```
### `fct_rev()`: 水準の順番を反転させる
水準の順番を反転することは非常によくあります。たとえば、グラフの読みやすさのために、左右または上下を反転するケースがあります。既に何回も強調しましたように、名目変数は基本的にfactor型にすべきであり、ここで`fct_rev()`関数が非常に便利です。たとえば、`Pref2`の水準は50音順でありますが、これを反転し、`Pref6`という名の列として追加してみましょう。
```{r}
Score_df_f1 <- Score_df_f1 |>
mutate(Pref6 = fct_rev(Pref2))
levels(Score_df_f1$Pref6)
```
関数一つで水準の順番が反転されました。
### `fct_infreq()`: 頻度順に順番を変更する
続いて、水準の順番を頻度順に合わせる`fct_infreq()`関数です。たとえば、`Score`が欠損でないケースのみで構成された`df2`を考えてみましょう。
```{r}
df2 <- df |>
filter(!is.na(Score))
```
そして、都府県ごとのケース数を計算します。
```{r}
table(df2$Pref)
```
ここで`Pref`をfactor化しますが、水準の順番を店舗数が多い方を先にするにはどうすれば良いでしょうか。`fct_infreq()`関数は指定された変数の各値の個数を計算し、多い順にfactorの水準を調整します。
```{r}
df2 <- df2 |>
# 多く出現した値順でfactor化する
mutate(Pref = fct_infreq(Pref))
levels(df2$Pref) # df2のPref変数の水準を出力
```
`"東京都"`、`"神奈川県"`、`"大阪府"`、...の順で水準の順番が調整され、これは`table(df$Pref2)`の順位とも一致します。
### `fct_inorder()`: データ内の出現順番に順番を変更する
続いて、`fct_inorder()`ですが、これは意外と頻繁に使われる関数です。たとえば、自分でデータフレームなどを作成し、ケースの順番も綺麗に整えたとします。しかし、既に指摘した通り、データフレーム (または、tibble)での順番とグラフにおける順番は一致するとは限りません。データフレームに格納された順番でfactorの水準が設定できれば非常に便利でしょう。そこで使うのが`fct_inorder()`です。
たとえば、`df`の`Pref`は`"東京都"`が1000個並び、続いて`"神奈川県"`が1000個、`"千葉県"`が1000個、...の順番で格納されています。この順番をそのままfactorの順番にするには以下のように書きます。
```{r}
df3 <- df |>
# Pref変数をfactor化し、水準は出現順とする
# 変換後の結果はPrefに上書きする
mutate(Pref = fct_inorder(Pref))
levels(df3$Pref)
```
### `fct_shift()`: 水準の順番をずらす
続いて、水準の順番をずらす`fct_shift()`関数を紹介します。たとえば、「1:そう思う」〜「5:そう思わない」、「9:答えたくない」の6水準で構成された変数があるとします。
```{r}
df4 <- tibble(
ID = 1:10,
Q1 = c(1, 5, 3, 2, 9, 2, 4, 9, 5, 1)
)
df4 <- df4 |>
mutate(Q1 = factor(Q1, levels = c(1:5, 9),
labels = c("そう思う",
"どちらかと言えばそう思う",
"どちらとも言えない",
"どちらかと言えばそう思わない",
"そう思わない",
"答えたくない")))
df4
```
水準の順番も「そう思う」〜「答えたくない」順で綺麗に整っています。この水準を反転するには`fct_rev()`関数が便利です。`Q1`の水準を反転した変数を`Q1_R`という新しい列として追加し、水準を確認してみましょう。
```{r}
df4 <- df4 |>
mutate(Q1_R = fct_rev(Q1))
df4
levels(df4$Q1_R)
```
「答えたくない」が最初の順番に来ましてね。できれば、「そう思わない」〜「そう思う」、「答えたくない」の順番にしたいところです。ここで使うのが`fct_shift()`ですが、書き方がややこしいので、噛み砕いて解説します。
```{r}
#| eval: false
# fct_shift()の使い方
データ名 |>
mutate(新しい変数名 = fct_shift(元の変数名, n = 左方向へずらす個数))
```
問題は`n =`引数ですが、その挙動については @tbl-factor1 を参照してください。
```{r tbl-factor1}
#| echo: false
#| message: false
#| tbl-cap: "`fct_shift()`の仕組み"
tibble(n = c("`n = -2`", "`n = -1`", "`n = 0`",
"`n = 1`", "`n = 2`"),
First = c("`E`", "`F`", "`A`", "`B`", "`C`"),
Second = c("`F`", "`A`", "`B`", "`C`", "`D`"),
Third = c("`A`", "`B`", "`C`", "`D`", "`E`"),
Fourth = c("`B`", "`C`", "`D`", "`E`", "`F`"),
Fifth = c("`C`", "`D`", "`E`", "`F`", "`A`"),
Sixth = c("`D`", "`E`", "`F`", "`A`", "`B`")) |>
gt() |>
cols_label("n" = "", "First" = "1番目", "Second" = "2番目",
"Third" = "3番目", "Fourth" = "4番目",
"Fifth" = "5番目", "Sixth" = "6番目") |>
fmt_markdown(columns = everything())
```
具体的には水準は左方向へ`n`個移動します。元の水準が`A`, `B`, `C`, ..., `F`の順で、`n = 1`の場合、`A`が`F`の後ろへ移動し、`B`, `C`, `D`, `E`, `F`が前の方へ1つずつ移動します。逆に右側へ1つ移動したい場合は`n = -1`のように書きます。今回は最初の水準を最後に移動させたいので、`n = 1`と指定します。
```{r}
df4 <- df4 |>
# Q1_Rの水準を左方向で1ずらす
mutate(Q1_R = fct_shift(Q1_R, n = 1))
levels(df4$Q1_R)
```
これで水準の反転が完了しました。`fct_shift()`はこのように世論調査データの処理に便利ですが、他にも曜日の処理に使えます。例えば、1週間の始まりを月曜にするか日曜にするかによって、`fct_shift()`を使うケースがあります。
### `fct_shuffle()`: 水準の順番をランダム化する
あまり使わない機能ですが、水準の順番をランダム化することも可能です。使い方は非常に簡単で、`fct_shuffle()`に元の変数名を入れるだけです。たとえば、`Score_df`の`Pref`の順番をランダム化し、`Pref2`として追加します。同じことをもう2回繰り返し、それぞれ`Pref3`と`Pref4`という名前で追加してみましょう。
```{r}
Score_df <- Score_df |>
mutate(Pref2 = fct_shuffle(Pref),
Pref3 = fct_shuffle(Pref),
Pref4 = fct_shuffle(Pref))
Score_df
levels(Score_df$Pref2)
levels(Score_df$Pref3)
levels(Score_df$Pref4)
```
`Pref`から`Pref4`まで同じように見えますが、水準の順番が異なります (`Pref`はcharacter型だから水準がありません)。
### `fct_reorder()`: 別の1変数の値を基準に水準の順番を変更する
`fct_infreq()`は出現頻度順に並び替える関数でしたが、それと似たような関数として`fct_reorder()`があります。ただし、これは出現頻度を基準にするのではなく、ある変数の平均値が低い順、中央値が高い順などでソートされます。まずは使い方から確認します。
```{r}
#| eval: false
データ名 |>
mutate(新しい変数名 = fct_reorder(元の変数名, 基準となる変数,
関数名, 関数の引数))
```
必要な引数が多いですね。解説よりも実際の例を見ながら説明します。今回も`Pref`をfactor変数にし、`Pref_R`という列で格納しますが、平均予算が安い順でfactorの水準を決めたいと思います。
```{r}
df <- df |>
mutate(Pref_R = fct_reorder(Pref, Budget, mean, na.rm = TRUE))
levels(df$Pref_R)
```
`Pref_R`の水準は千葉県、埼玉県、奈良県、...の順ですが、本当にそうでしょうか。`group_by()`と`summarise()`などを使って確認してみましょう。
```{r}
df |>
group_by(Pref) |>
summarise(Budget = mean(Budget, na.rm = TRUE),
.groups = "drop") |>
arrange(Budget)
```
問題なくソートされましたね。注意点としては`fct_reorder()`内に関数名を書く際、`()`は不要という点です。関数名の次の引数としてはその関数に別途必要な引数を指定します。引数が省略可能、あるいは不要な関数を使う場合は、省略しても構いませんし、数に制限はありません。
また、低い順ではなく、高い順にすることも可能です。次は`Score`の中央値が高い順に水準を設定した`Pref_R2`を作ってみましょう。
```{r}
df <- df |>
mutate(Pref_R2 = fct_reorder(Pref, Score, median, na.rm = TRUE, .desc = TRUE))
levels(df$Pref_R2)
```
変わったのは`mean`の代わりに`median`を使ったこと、そして`.desc`引数が追加された点です。`fct_reorder()`には`.desc = FALSE`がデフォルトとして指定されており、省略した場合は昇順でfactorの水準が決まります。ここで`.desc = TRUE`を指定すると、降順となります。実際、`Score`の中央値順になっているかを確認してみましょう。
```{r}
df |>
group_by(Pref) |>
summarise(Score = median(Score, na.rm = TRUE),
.groups = "drop") |>
arrange(desc(Score))
```
### `fct_reorder2()`: 別の2変数の値を基準に水準の順番を変更する {#sec-factor-forcat-reorder2}
この関数は別の変数を基準に水準が調整される点では`fct_reorder()`と類似しています。ただし、よく誤解されるのは「変数Aの値が同じなら変数Bを基準に...」といったものではありません。たとえば、`fct_reorder(x, y, mean)`の場合、`y`の平均値 (`mean()`)の順で`x`の水準を調整するという意味です。この`mean()`関数に必要なデータはベクトル1つです。しかし、関数によっては2つの変数が必要な場合があります。
これは頻繁に直面する問題ではありませんが、この`fct_reorder2()`関数が活躍するケースを紹介します。以下は6月27日から7月1日までの5日間、5地域におけるCOVID-19新規感染者数を表したデータです[^fct_reorder2]。入力が面倒な方は[ここ](Data/COVID19.csv)からダウンロードして読み込んでください。
[^fct_reorder2]: データの出典は[Google](https://news.google.com/covid19/map)です。
```{r}
# 入力が面倒ならデータをダウンロードし、
# Reorder2_df <- read_csv("Data/COVID19.csv")
Reorder2_df <- tibble(
Country = rep(c("日本", "韓国", "中国 (本土)", "台湾", "香港"),
each = 5),
Date = rep(c("2020/06/27", "2020/06/28", "2020/06/29",
"2020/06/30", "2020/07/01"), 5),
NewPat = c(100, 93, 86, 117, 130,
62, 42, 43, 50, 54,
17, 12, 19, 3, 5,
0, 0, 0, 0, 0,
1, 2, 4, 2, 28)
)
Reorder2_df <- Reorder2_df |>
mutate(Date = as.Date(Date))
Reorder2_df
```
可視化のコードはとりあえず無視し、グラフを出力してみましょう。
```{r}
#| fig-height: 3.5
Reorder2_df |>
ggplot() +
geom_line(aes(x = Date, y = NewPat, color = Country),
size = 1) +
scale_x_date(date_labels = "%Y年%m月%d日") +
labs(x = "年月日", y = "新規感染者数 (人)", color = "")
```
このグラフに違和感はあまりありませんが、「読みやすさ」の麺では改善の余地があります。たとえば、7月1日の時点で、新規感染者数が多いのは日本、韓国、香港、中国 (本土)、台湾の順です。しかし、右側の凡例の順番はそうではありません。この順番が一致すれば、更に図は読みやすくなるでしょう。
```{r}
factor(Reorder2_df$Country)
```
実際、何も指定せずに`Reorder2_df`の`Country`をfactor化すると、韓国、香港、台湾、...の順であり、これは上のグラフと一致します。これをグラフにおける7月1日の新規感染者数の順で並べるためには、`Date`を昇順にソートし、そして最後の要素 (`"2020/07/01"`)内で新規感染者数 (`NewPat`)を降順に並べ替えた場合の順番にする必要があります。実際、`Reorder2_df`を`Date`で昇順、`NewPat`で降順にソートし、最後の5行を抽出した結果が以下のコードです。
```{r}
Reorder2_df |>
arrange(Date, desc(NewPat)) |>
slice_tail(n = 5)
```
このように、水準を調整する際に2つの変数 (`Date`と`NewPat`)が使用されます。`fct_reorder2()`は`fct_reorder()`と買い方がほぼ同じですが、基準となる変数がもう一つ加わります。
```{r}
#| eval: false
データ名 |>
mutate(新しい変数名 = fct_reorder2(元の変数名,
基準となる変数1, 基準となる変数2,
関数名, 関数の引数))
```
重要なのはここの関数のところですが、`fct_reorder2()`はデフォルトで`last2()`という関数が指定されており、まさに私たちに必要な関数です。したがって、ここでは関数名も省略できますが、ここでは一応明記しておきます。
```{r}
Reorder2_df <- Reorder2_df |>
mutate(Country2 = fct_reorder2(Country, Date, NewPat, last2))
```
それでは新しく出来た`Country2`の水準を確認してみましょう。
```{r}
levels(Reorder2_df$Country2)
```
ちゃんと7月1日の新規感染者数基準で水準の順番が調整されましたので、これを使ってグラフをもう一回作ってみます。
```{r}
#| fig-height: 3.5
Reorder2_df |>
ggplot() +
geom_line(aes(x = Date, y = NewPat, color = Country2),
size = 1) +
scale_x_date(date_labels = "%Y年%m月%d日") +
labs(x = "年月日", y = "新規感染者数 (人)", color = "")
```
これで図がさらに読みやすくなりました。ちなみに、{forcatsパッケージは`last2()`以外にも`first2()`という関数も提供しております。これを使うと、7月1日でなく、6月27日の新規感染者数の降順で水準の順番が調整されます。他にも引数を2つ使用する自作関数も使えますが、`fct_reorder2()`の主な使いみちは`last2()`で十分でしょう。
### `fct_collapse()`: 水準を統合する
水準数をより水準数に減らすためには、`fct_recode()`を使います。先ほど、`fct_shift()`で使った`df4`の例を考えてみましょう。`df4`の`Q1`の水準数は6つです。
```{r}
levels(df4$Q1)
```
これを4つに減らして見ましょう。具体的には「そう思う」と「どちらかと言えばそう思う」を「そう思う」に、「そう思わない」と「どちらかと言えばそう思わない」を「そう思わない」に統合します。これを`fct_recode()`で処理したのが以下のコードです。
```{r}
# fct_recode()を使った例
df4 <- df4 |>
mutate(Q1_R2 = fct_recode(Q1,
そう思う = "そう思う",
そう思う = "どちらかと言えばそう思う",
どちらとも言えない = "どちらとも言えない",
そう思わない = "どちらかと言えばそう思わない",
そう思わない = "そう思わない",
答えたくない = "答えたくない"))
df4
levels(df4$Q1_R2)
```
しかし、水準を統合するに特化した`fct_collapse()`を使えばより便利です。使い方は、`fct_recode()`に非常に似ているため省略しますが、`=`の右側を`c()`でまとめることが出来ます。
```{r}
# fct_collapse()を使った例
df4 <- df4 |>
mutate(Q1_R3 = fct_collapse(Q1,
そう思う = c("そう思う", "どちらかと言えばそう思う"),
どちらとも言えない = "どちらとも言えない",
そう思わない = c( "どちらかと言えばそう思わない", "そう思わない"),
答えたくない = "答えたくない"))
df4
levels(df4$Q1_R3)
```
`fct_recode()`の結果と同じ結果が得られました。元の水準数や、減らされる水準数などによっては書く手間があまり変わらないので、好きな方を使っても良いでしょう。
### `fct_drop()`: 使われていない水準を除去する
水準としては存在するものの、データとしては存在しないケースもあります。これをここでは「空水準 (empty levels)」と呼びます。たとえば、以下のコードは`Pref`をfactor化してから`Pref == "奈良県"`のケースを落としたものです。
```{r}
Score_df_f2 <- df |>
mutate(Pref = fct_inorder(Pref)) |>
filter(Pref != "奈良県") |>
group_by(Pref) |>
summarise(Score = mean(Score, na.rm = TRUE),
.groups = "drop")
Score_df_f2
```
このように結果としては、奈良県のデータを除外したため空水準である奈良県は表示されませんが、`Pref`変数はどうでしょうか。
```{r}
levels(Score_df_f2$Pref)
```
このように水準としては残っていることが分かります。使われていない水準が分析や可視化に影響を与えないケースもありますが、与えるケースもあります。これもこれまで勉強してきた`fct_*()`関数群で対応可能ですが、`fct_drop()`関数を使えば一発で終わります。実際にやってみましょう。
```{r}
Score_df_f2 <- Score_df_f2 |>
mutate(Pref = fct_drop(Pref))
```
```{r}
levels(Score_df_f2$Pref)
```
水準から奈良県が消えました。同じ機能をする関数としてはR内蔵関数である`droplevels()`関数があり、使い方は`fct_drop()`と同じです。
### `fct_expand()`: 水準を追加する
一方、空水準を追加することも可能です。`fct_expand()`関数には元の変数名に加え、追加する水準名を入れるだけです。たとえば、`df`の`Pref`の水準は関東と関西の9都府県名となっていますが、ここに`"滋賀県"`という水準を追加してみます。。
```{r}
df5 <- df |>
mutate(Pref = fct_expand(Pref, "滋賀県"))
levels(df5$Pref)
```
`"滋賀県"`という新しい水準が出来ましたね。ただし、新しく追加された水準は最後の順番になりますので、修正が必要な場合は`fct_relevel()`などを使って適宜修正してください。
新しく水準が追加されることによって、何かの変化はあるでしょうか。まずは都府県ごとに`Score`の平均値とケース数を計算してみましょう。
```{r}
df5 |>
group_by(Pref) |>
summarise(Score = mean(Score, na.rm = TRUE),
N = n(),
.groups = "drop")
```
見た目は全く変わらず、滋賀県の行が新しく出来たわけでもありません。{dplyr}の`group_by()`の場合、空水準はグループ化の対象になりません。一方、多くのR内蔵関数はケースとして存在しなくても計算の対象となります。たとえば、ベクトル内のある値が何個格納されているか確認する`table()`関数の例を見てみましょう。
```{r}
table(df5$Pref)
```
`"滋賀県"`という列があり、合致するケースが0と表示されます。`group_by()`でも空の水準まで含めて出力する引数`.drop`があります。デフォルトは`TRUE`ですが、これを`FALSE`に指定してみます。
```{r}
df5 |>
group_by(Pref, .drop = FALSE) |>
summarise(Score = mean(Score, na.rm = TRUE),
N = n(),
.groups = "drop")
```
空水準も出力され、`Score`の平均値は計算不可 (`NaN`)、ケース数は0という結果が得られました。
### `fct_explicit_na()`: 欠損値に水準を与える
まずは、実習用データ`df6`を作ってみまます。`X1`はnumeric型変数ですが、これをfactor化します。最初から`tibble()`内でfactor化しておいても問題ありませんが、練習だと思ってください。
```{r}
df6 <- tibble(
ID = 1:10,
X1 = c(1, 3, 2, NA, 2, 2, 1, NA, 3, NA)
)
df6 <- df6 |>
mutate(X1 = factor(X1,
levels = c(1, 2, 3),
labels = c("ラーメン", "うどん", "そば")))
df6
```
それでは`X1`をグループ化変数とし、ケース数を計算してみましょう。
```{r}
df6 |>
group_by(X1) |>
summarise(N = n(),
.groups = "drop")
```
`NA`もグループ化の対象となります。以下はこの欠損値も一つの水準として指定する方法について紹介します。欠損値を欠損値のままにするケースが多いですが、欠損値が何らかの意味を持つ場合、分析の対象になります。たとえば、多項ロジスティック回帰の応答変数として「分からない/答えたくない」を含めたり、[「分からない/答えたくない」を選択する要因を分析](https://ci.nii.ac.jp/naid/40021269699)したい場合は、欠損値に値を与える必要があります。なぜなら、一般的な分析において欠損値は分析対象から除外されるからです。
まずは、これまで紹介した関数を使ったやり方から紹介します。
```{r}
df6 |>
# まず、X1をcharacter型に変換し、X2という列に保存
mutate(X2 = as.character(X1),
# X2がNAなら"欠損値"、それ以外なら元のX2の値に置換
X2 = ifelse(is.na(X2), "欠損値", X2),
# X2を再度factor化する
X2 = factor(X2,
levels = c("ラーメン", "うどん", "そば", "欠損値")))
```
`X1`をcharacter型に戻す理由[^fct_explicit_na1]は、水準にない値が入るとfactor化が解除されるからです。factor型をcharacter型に戻さずに`df6$X1`の`NA`を`"欠損値"`に置換すると、以下のようになります。
[^fct_explicit_na1]: character型でなく、numeric型でも出来ます。
```{r}
# df6のX1がNAなら"欠損"、それ以外なら元のX1の値を返す
ifelse(is.na(df6$X1), "欠損値", df6$X1)
```
`"ラーメン"`と`"うどん"`、`"そば"`がfactor化前の1, 2, 3に戻っただけでなく、`NA`が`"欠損値"`というcharacter型に置換されたため、全体がcharacter型に変換されました。このように欠損値に水準を与える作業は難しくはありませんが、面倒な作業です。そこで登場する関数が`fct_exlpicit_na()`関数です。使い方は、元の変数に加え、欠損値の水準名を指定する`na_level`です。
```{r}
df6 <- df6 |>
# na_levelのデフォルト値は"(Missing)"
mutate(X2 = fct_explicit_na(X1, na_level = "欠損値"))
df6
```
欠損値が一つの水準になったことが分かります。
```{r}
df6 |>
group_by(X2) |>
summarise(N = n(),
.groups = "drop")
```
むろん、`group_by()`を使ってもちゃんと出力されます。