-
Notifications
You must be signed in to change notification settings - Fork 6
/
datatype.qmd
776 lines (562 loc) · 38.6 KB
/
datatype.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
770
771
772
773
774
775
776
# データ型 {#sec-datatype}
```{r}
#| label: datatype-common
#| include: false
source("_common.R")
```
## データ型とは {#sec-type-intro}
この章ではRにおけるデータ型について説明する。第[-@sec-datastructure]章で説明するデータ「構造」とデータ「型」は異なる概念なので、混同しないようにしてほしい。次章でも説明するが、Rにおけるデータの最小単位はベクトルである。`c(1, 2, 3, 4, 5)`や`c("Yanai", "Song", "Hadley")`がベクトルであることは(ここまで読んできた読者には)明らかだが、`30`や`"R"`もベクトルなのだ。要素が1つのみのベクトルは原子ベクトル (atomic vector) とも呼ばれるが、その性質は要素が2つ以上のベクトルと何ら変わらない。
これらのベクトルの要素の性質を表すのが、データ型である。たとえば`c(2, 3, 5, 7, 11)`は数値型、`c("R", "Python", "Julia")`は文字型と呼ばれる。第[-@sec-rbasic]章で紹介した`FALSE`や`TRUE`は論理型である。ベクトルは複数の要素で構成されることもあるが、1つのベクトルに含まれるすべての要素は必ず**同じデータ型**である。
データ分析を扱うのはベクトルばかりではない。ベクトル以外で代表的なものとして、行列や表がある。しかし、Rでは、行列や表についてもそれらの中身である1つ1つの要素は長さ1のベクトルなのだ。たとえば、以下のような2行5列の行列があるとしよう。ここで第1行第3列の要素は`11`であり、これは長さ1の数値型ベクトルである。あるいは5列目は`c(23, 29)`であり、これは長さ2の数値型ベクトルである。
```{r}
#| label: datatype-intro-1
#| echo: false
matrix(c(2, 3, 5, 7, 11, 13, 17, 19, 23, 29),
nrow = 2)
```
表についてはどうだろうか。以下の表の第3行第2列の要素は`"American Samoa"`という長さ1の文字型ベクトルである。また、6列目は`c("UEFA", "CAF", "OFC", "UEFA", "CAF", "CONCACAF")`であり、これは長さ6の文字型ベクトルだ。このようにRで扱うすべてのデータは、複数のベクトルが集まってできている。
```{r}
#| label: datatype-intro-2
#| echo: false
head(read.csv("Data/FIFA_Women.csv"))
```
つまり、複数のベクトルを整列させて集めたのが行列や表であり、その整列の仕方がデータ構造を決める。データ型ごとの処理方法については本書を通じて紹介するので、本章は適当に読み飛ばしも大きな問題はない。ただし、Factor型とDate & Datetime型の処理はやや特殊なので、手を動かしながら読み進めてほしい。
繰り返しになるが、本章ではデータ型について解説し、データ構造については次章で扱う。
## Logical {#sec-type-logical}
Logical型(論理型)は`TRUE`と`FALSE`のみで構成されたデータ型である。練習として長さ5のlogical型ベクトルを作ってみよう。
```{r}
#| label: datatype-logical-1
logical_vec1 <- c(TRUE, FALSE, TRUE, TRUE, FALSE)
logical_vec1
```
ここで、
1 . `TRUE`と`FALSE`は`"`で囲まない
2. `TRUE`と`FALSE` はすべて大文字
という2点に注意されたい。`"TRUE"`、`"FALSE"`と入力したり、`True` あるいは `false` などと入力してしまうと、logical型とは認識されない。入力ミスで2番目の要素である`FALSE`を`"FALSE"`と入力したらどうなるだろうか。
```{r}
#| label: datatype-logical-2
logical_vec2 <- c(TRUE, "FALSE", TRUE, TRUE, FALSE)
logical_vec2
```
2番目の要素だけでなく、他の全ての要素も`"`で囲まれた。それが何を意味するのかを確かめるために、この2つのベクトルのデータ型を確認してみよう。ベクトルのデータ型を確認するには、`class()`関数を使う。
```{r}
#| label: datatype-logical-3
class(logical_vec1)
class(logical_vec2)
```
`logical_vec1`はlogical型だが、`logical_vec2`はcharacter型(文字列型)と認識されていることがわかる。同様に、`is.logical()`関数を使ってあるベクトルがlogical型か否かを判定することもできる。ベクトルがlogical型なら`TRUE`が、logical型以外なら`FALSE`が返される。
```{r}
#| label: datatype-logical-4
is.logical(logical_vec1)
is.logical(logical_vec2)
```
これは、「1つのベクトルに含まれるすべての要素のデータ型は同じでなければならない」というRのルールに則った挙動である。`logical_vec2` にベクトルを格納する際、誤って`"FALSE"`と引用符付きで入力したことにより、`"FALSE"`はcharacter型として「正しく」認識された。1つのベクトルにlogical型とcharacter型が共存することはできないので、すべての要素がcharacter型になったのだ。そうなったのは、logical型(`TRUE`または`FALSE`の2種類の値しかない)は`"TRUE"`または`"FALSE"`という文字列として捉えることができる一方で、一般的に文字列(たとえば、`"SONG"` や `"YANAI"`)の真偽 (`TRUE` or `FALSE`) を決めることはできないからである。
Logical型は様々な場面で使われるが、特によく使うのは第[-@sec-rbasic-calc]章で紹介する要素の抽出と、第[-@sec-programming]章で紹介する条件分岐 (`if()`や`ifelse()`)、条件反復 (`while()`) においてである。
## Numeric {#sec-type-numeric}
Numeric型(数値型)は、その名のとおり数値を表す型である。試しにnumeric型のベクトル`numeric_vec1`を作成し、データ型を確認してみよう
```{r}
#| label: datatype-numeric-1
numeric_vec1 <- c(2, 0, 0, 1, 3)
class(numeric_vec1)
```
`is.logical()`同様、`is.numeric()`関数も用意されている。
```{r}
#| label: datatype-numeric-2
is.numeric(numeric_vec1)
is.numeric(logical_vec1)
```
もう少し詳しく分けると、numeric型の中にはinteger型(整数型)とdouble型(倍精度浮動小数点型、簡単にいえば実数)がある。これらを意識的に区別して使う場面は稀なので、型の詳細に興味がなければ読み飛ばしてもよい。
Integerは整数であり、doubleは実数である。これらの区別は`class()`関数では確認できない。代わりに`typeof()`関数を使う。
```{r}
#| label: datatype-numeric-3
typeof(numeric_vec1)
```
自分で作成するnumeric型のベクトルはすべてdouble型になる。整数型のベクトルを作成したいときは、数値の後ろに`L`を付けることで、それを整数として扱うことを宣言する。
```{r}
#| label: datatype-numeric-4
integer_vec1 <- c(2L, 0L, 0L, 1L, 3L)
typeof(integer_vec1)
```
ここで注意すべき点は、要素の中に1つでも`L`が付かないもの(つまり、整数よりも一般的な数値)が含まれる場合、データ型は自動的にdouble型になるということである。
```{r}
#| label: datatype-numeric-5
integer_vec2 <- c(2L, 0L, 0, 1L, 3L)
typeof(integer_vec2)
```
ここでもやはり、「1つのベクトルに含まれるすべての要素のデータ型は同じでなければならない」というRのルールが守られている。
当然ながら、小数点以下に0以外を含む数値に`L`を付けてもinteger型にはならず、それはdouble型となる。また、integer型同士の割り算の結果もdouble型になる。これは`2L/1L`のように数学的には結果として整数が得られる場合でも同じである。それに対し、integer型どうしの足し算、引き算、掛け算の結果はinteger型のままである。
```{r}
#| label: datatype-numeric-6
typeof(2.00L)
typeof(2.03L)
typeof(3L / 12L)
typeof(3L / 1L)
typeof(3.0L + 1L)
typeof(3L - 4L)
typeof(3L * 6L)
```
一般的な分析において整数と実数を厳格に区別して使う場面は多くないので、(少なくとも当面は)integer型とdouble型の違いはあまり気にせず、numeric型であることさえわかっていれば問題ないだろう。
## Complex {#sec-type-complex}
Complex型(複素数型)は複素数を表すデータ型であり、`実数部 + 虚数部i`のように表記する。複素数のベクトル`complex_vec1`を作成し、データ型を確認してみよう。
```{r datatype-complex-1}
complex_vec1 <- c(1 + 3i, 3 + 2i, 2.5 + 7i)
complex_vec1
class(complex_vec1)
```
あまりおすすめはできないが、`虚数部i + 実数部`のように書くこともできる。
```{r datatype-complex-2}
complex_vec2 <- c(3i + 1, 2i + 3, 7i + 2.5)
complex_vec2
class(complex_vec2)
```
`complex_vec1`と`complex_vec2`は同じベクトルであることを確認しよう。
```{r datatype-complex-3}
complex_vec1 == complex_vec2
```
1つのベクトル内にnumeric型とcomplex型を混在させようとすると、すべのて要素が強制的にcomplex型に変換される。numeric型から自動的に変換されたcomplex型の値は、`実数部+0i`と表記される。
```{r datatype-complex-4}
complex_vec3 <- c(2 + 7i, 5, 13 + 1i)
complex_vec3
class(complex_vec3)
```
## Character {#sec-type-character}
Character型(文字列型)は文字列で構成されるデータ型である。Rを含む多くの言語は文字列を表現するために、値を引用符`"`で囲む。たとえば、`"abc"`はcharacter型であり、`"1"`や`"3 + 5i"`もcharacter型である。数字であっても`"`で囲めばそれは文字列として扱われる。Character型のベクトルをいくつか作ってみよう。
```{r datatype-character-1}
char_vec1 <- c("Shigemura", "Song", "Tani", "Yanai")
char_vec2 <- c(1, 2, 3, 4)
char_vec3 <- c("1", "2", "3", "4")
char_vec4 <- c(1, "2", 3, 4)
char_vec1
char_vec2
char_vec3
char_vec4
```
`char_vec2`と`char_vec3`の違いは数字を`"`で囲むか否かにある。ベクトルの中身を表示しても、`char_vec2`の要素は`"`で囲まれていないことがわかる。それぞれのデータ型も確認しよう。
```{r datatype-character-2}
class(char_vec1)
class(char_vec2)
class(char_vec3)
class(char_vec4)
```
やはり`char_vec3`がcharacter型になっていることが分かる。そして、1つの要素だけが`"`で囲まれた `char_vec4` もcharacter型になっている。「1つのベクトルに含まれるすべての要素のデータ型は同じでなければならない」のだ。
## Factor {#sec-type-factor}
Factor型(因子型)は、ラベル付きの数値型データである。Factor型の見た目はcharacter型とほぼ同じであり、分析の場面においてもcharacter型と同じように扱われる。Character型との違いは、factor型には「順序が付いている」という点にある。例えば、以下の質問文に対するアンケートの結果をデータとして扱う場面を考えよう。
* あなたは猫が好きですか。
1. めちゃめちゃ好き
2. めちゃ好き
3. 好き
4. どちらかといえば好き
以下の @tbl-datatype-factor-1 は5人の回答結果である。
```{r tbl-datatype-factor-1}
#| echo: false
#| tbl-cap: "猫好きの度合い"
data.frame(ID = 1:5,
Name = c("Yanai", "Song", "Shigemura", "Tani", "Hadley"),
Cat = c("めちゃめちゃ好き",
"めちゃめちゃ好き",
"どちらかといえば好き",
"めちゃ好き",
"好き")) %>%
kable(booktabs = TRUE,
align = "cll") %>%
kable_styling(bootstrap_options = "striped",
full_width = FALSE,
position = "center",
latex_options = "hold_position") %>%
row_spec(0, align = "c")
```
表を読むことができる人間なら、この表から[重村](https://soheishigemura.com)なる人物がどれだけ猫が嫌いなのかが分かる!ただし、Rはそうではない。Rは日本語どころか人間が使う自然言葉をそのまま理解することができない。そのため、「めちゃめちゃ好き」と「どちらかといえば好き」が異なる値であることは理解できても、それらの選択肢に順位を付けることができない。よって、各項目に明示的に順番を付けてあげる必要があり、そのために使われるのがfactor型である。
実習のために @tbl-datatype-factor-1 の`Cat`列のみのベクトルを作ってみましょう。
```{r datatype-factor-2}
factor_vec1 <- c("めちゃめちゃ好き", "めちゃめちゃ好き",
"どちらかといえば好き", "めちゃ好き", "好き")
factor_vec1
class(factor_vec1)
```
`factor_vec1`は普通の文字列ベクトルであることが分かります。これをfactor型に変換するためには`factor()`関数を使います。
```{r datatype-factor-3}
factor_vec2 <- factor(factor_vec1, ordered = TRUE,
levels = c("どちらかといえば好き", "好き",
"めちゃ好き", "めちゃめちゃ好き"))
class(factor_vec2)
```
データ型がfactor型に変換されています。`"ordered"`というものも付いていますが、これについては後ほど説明します。それでは中身をみましょう。
```{r datatype-factor-4}
factor_vec2
```
いくつかの点で異なります。まず、文字列であるにもかかわらず、`"`で囲まれていいない点です。そして3行目に`4 Levels: `というのが追加されている点です。このlevelは「水準」と呼ばれるものです。`4 Levels`ですから、`factor_vec2`は4つの水準で構成されていることを意味します。Factor型の値は予め指定された水準以外の値を取ることはできません。たとえば、2番目の要素を「超好き」に変えてみましょう。
```{r datatype-factor-5}
factor_vec2[2] <- "超好き"
factor_vec2
```
警告が表示され、2番目の要素が後ほど紹介する欠損値となっていることが分かります。それでは普通に「好き」を入れてみましょう。
```{r datatype-factor-6}
factor_vec2[2] <- "好き"
factor_vec2
```
今回は問題なく置換できましたね。このようにfactor型の取りうる値は既に指定されています。また、`## 4 Levels: どちらかといえば好き < 好き < ... < めちゃめちゃ好き`からも分かるように、その大小関係の情報も含まれています。猫好きの度合いは「どちらかといえば好き-好き-めちゃ好き-めちゃめちゃ好き」の順で高くなることをRも認識できるようになりました。
Factor型はこのように順序付きデータを扱う際に便利なデータ型ですが、順序情報を含まないfactor型もあります。これは`factor()`を使う際、`ordered = TRUE`引数を削除するだけでできます。
```{r datatype-factor-7}
factor_vec3 <- factor(factor_vec1,
levels = c("どちらかといえば好き", "好き",
"めちゃ好き", "めちゃめちゃ好き"))
factor_vec3
class(factor_vec3)
```
今回は3行目が`## Levels: どちらかといえば好き 好き めちゃ好き めちゃめちゃ好き`となり、順序に関する情報がなくなりました。また、`class()`で確認しましたデータ型に`"ordered"`が付いていません。これは順序なしfactor型であることを意味します。「順序付けしないならfactor型は要らないのでは...?」と思うかも知れませんが、これはこれで便利です。その例を考えてみましょう。
分析においてfactor型はcharacter型に近い役割を果たしますが、factor型なりの長所もあります。それは図や表を作成する際です。例えば、横軸が都道府県名で、縦軸がその都道府県の財政力指数を表す棒グラフを作成するとします。たとえば、 @tbl-datatype-factor-8 のようなデータがあるとします。このデータは3つの列で構成されており、`ID`と`Zaisei`列はnumeric型、`Pref`列はcharacter型です。
```{r tbl-datatype-factor-8}
#| echo: false
#| tbl-cap: "5都道府県のH29財政力指数"
zaisei_df <- data.frame(
ID = 1:5,
Pref = c("Hokkaido", "Tokyo", "Aichi", "Osaka", "Fukuoka"),
Zaisei = c(0.44396, 1.19157, 0.92840, 0.78683, 0.64322)
)
zaisei_df %>%
kable(booktabs = TRUE,
align = "llr",
col.names = c("ID", "都道府県", "財政力指数")) %>%
kable_styling(bootstrap_options = "striped",
full_width = FALSE,
position = "center",
latex_options = "hold_position") %>%
row_spec(0, align = "c")
```
可視化については第[-@sec-visualization1]章以降で詳しく解説しますが、この`Pref`列をcharacter型にしたままグラフにしますと @fig-datatype-factor-9 のようになります。
```{r fig-datatype-factor-9}
#| echo: false
#| fig-cap: "5都道府県のH29財政力指数"
zaisei_df %>%
ggplot() +
geom_bar(aes(x = Pref, y = Zaisei), stat = "identity") +
labs(x = "都道府県", y = "H29 財政力指数") +
theme_gray(base_size = 12)
```
このようにアルファベット順で横軸が並び替えられます。別にこれでも問題ないと思う方もいるかも知れませんが、基本的に日本の都道府県は北から南の方へ並べるのが一般的な作法です[^PrefSort]。北海道と東京、大阪の間には順序関係はありません。しかし、表示される順番は固定したい。この場合、`Pref`列を順序なしfactor型にすれば良いです[^orderedfactor]。データフレームの列を修正する方法は第[-@sec-datastructure]章で詳しく説明します。
[^PrefSort]: アメリカの州ならアルファベット順ですね。
[^orderedfactor]: むろん、「北から南へ」という規則もあるので、順序付きfactor型にしても問題ありません。ただし、今回はあくまでも表示順番を設定したいだけですので、`ordered = TRUE`は省略しました。
```{r datatype-factor-10}
zaisei_df$Pref <- factor(zaisei_df$Pref,
levels = c("Hokkaido", "Tokyo", "Aichi", "Osaka", "Fukuoka"))
```
`zaisei_df`の`Pref`列をfactor型にしてから同じ図を描くと @fig-datatype-factor-11 のようになります。
```{r fig-datatype-factor-11}
#| echo: false
#| fig-cap: "5都道府県のH29財政力指数"
zaisei_df %>%
ggplot() +
geom_bar(aes(x = Pref, y = Zaisei), stat = "identity") +
labs(x = "都道府県", y = "H29 財政力指数") +
theme_gray(base_size = 12)
```
都道府県以外にもこのような例は多くあります。順序尺度で測定された変数が代表的な例です。他にも政党名を議席数順で表示させたい場合もfactor型は有効でしょう。
## Date {#sec-type-date}
### なぜDate型があるのか
Date型は年月日を表すデータ型[^datetime]です。この2つのデータ型はかなり複雑ですが、ここでは簡単に説明します。Date型は日付の情報を含むため、順序関係が成立します。その意味では順序付きFactor型とあまり挙動は変わらないかもしれませんが、実際はそうではありません。
[^datetime]: 他にも時間まで表すDateTime型があります。
たとえば、Songの1週間[^sleep]の睡眠時間を記録したデータ`SongSleep`があるとします。`Date`という列には日付が、`Sleep`列には睡眠時間が記録されています。睡眠時間の単位は「分」です。
[^sleep]: 実際は2017年6月17日から11月15日まで記録しましたが、ここでは1週間分のみお見せします。
```{r datatype-date-1}
SongSleep <- data.frame(
Date = c("2017-06-17", "2017-06-18", "2017-06-19", "2017-06-20",
"2017-06-21", "2017-06-22", "2017-06-23"),
Sleep = c(173, 192, 314, 259, 210, 214, 290)
)
```
中身をみると、以下のようになります。
```{r datatype-date-2}
SongSleep
```
日付を横軸に、睡眠時間を縦軸にした散布図を描く @fig-datatype-date-3 のようになります。{ggplot2}を利用した作図については第[-@sec-visualization1]章で解説しますので、ここではDate型の特徴のみ理解してもらえたら十分です。
```{r fig-datatype-date-3}
#| fig-cap: "Songの睡眠時間"
ggplot(SongSleep,
mapping = aes(x = Date, y = Sleep)) +
geom_point() +
labs(x = "日付", y = "睡眠時間 (分)") +
theme_gray(base_size = 12)
```
この図は全く問題ないように見えます。それでは、`Date`列をそれぞれDate型に変換し、`SoongSleep`データの`DateD`としてみます。データフレームの列追加については第[-@sec-datastructure-dataframe]章で解説します。
```{r datatype-date-4}
SongSleep$DateD <- as.Date(SongSleep$Date)
```
中身を見てみますが、あまり変わっていないようです。`Date`と`DateD`列は全く同じように見えますね。
```{r datatype-date-5}
SongSleep
```
図にすると実は先ほどの図と同じものが得られます。
```{r datatype-date-6}
#| fig-cap: "Songの睡眠時間"
ggplot(SongSleep,
mapping = aes(x = DateD, y = Sleep)) +
geom_point() +
labs(x = "日付", y = "睡眠時間 (分)") +
theme_gray(base_size = 12)
```
しかし、Songがうっかり6月19日に記録するのを忘れたとします。つまり、`SongSleep`データの3行目が抜けている状況を考えてみましょう。データフレームの要素抽出については第[-@sec-datastructure-dataframe]章で解説します。
```{r datatype-date-7}
SongSleep2 <- SongSleep[-3, ]
```
中身をみると、以下のようになります。`Date`も`DateD`も同じように見えます。
```{r datatype-date-8}
SongSleep2
```
この状態で横軸を`Date`にしたらどうなるでしょうか( @fig-datatype-date-9 )。
```{r fig-datatype-date-9}
#| fig-cap: "Songの睡眠時間"
ggplot(SongSleep2,
mapping = aes(x = Date, y = Sleep)) +
geom_point() +
labs(x = "日付", y = "睡眠時間 (分)") +
theme_gray(base_size = 12)
```
一方、横軸を`DateD`にしたものが @fig-datatype-date-10 です。
```{r fig-datatype-date-10}
#| fig-cap: "Songの睡眠時間"
ggplot(SongSleep2,
mapping = aes(x = DateD, y = Sleep)) +
geom_point() +
labs(x = "日付", y = "睡眠時間 (分)") +
theme_gray(base_size = 12)
```
違いが分かりますかね。違いは抜けている6月19日です。 @fig-datatype-date-9 を見ると、横軸の6月18日の次が20日になっています。一方、 @fig-datatype-date-10 は19日になっており、ちゃんと空けてくれますね。これはDate型でない場合、データにないものは図に表示されないことを意味します。一方、Date型は抜けている日があっても、図に表示表示されます。一般のcharacter型またはfactor型でこのようなことを再現するためには、6月19日の列を追加し、睡眠時間を欠損値として指定する必要があります。たとえば、`SongSleep`データにおいて6月19日の行は温存したまま、睡眠時間だけを欠損値にしてみましょう。
```{r datatype-date-11}
SongSleep3 <- SongSleep
SongSleep3$Sleep[SongSleep$Date == "2017-06-19"] <- NA
SongSleep3
```
このように日付はあるが、睡眠時間が欠損している場合、図にしたものが @fig-datatype-date-12 です。
```{r fig-datatype-date-12}
#| fig-cap: "Songの睡眠時間"
ggplot(SongSleep3,
mapping = aes(x = Date, y = Sleep)) +
geom_point() +
labs(x = "日付", y = "睡眠時間 (分)") +
theme_bw()
```
横軸上に6月19日が表示されます。このようにDate型でなくてもDate型と同じように動かすことは可能ですが、非常に面倒です。その意味でDate型は時系列データを扱う際に非常に便利なデータ型です。
### Date型の作り方
Date型を作成方法はいくつかあります。
1. character型をDate型にする
2. numeric型をDate型にする
主に使う方法は1であり、既に前節でお見せしました`as.Date()`関数を使います。方法2もまた`as.Date()`を使いますが、これは「xxxx年xx月xx日から何日目」という書き方となり、起点となる日付 (`origin`)[^dateorigin]を指定する必要があります。
[^dateorigin]: 主に使う`origin`は1970年1月1日です。
ここでは方法1について解説します。日付を表すいくつかのベクトルを作ってみましょう。
```{r datatype-date-13}
Date1 <- "2020-05-21"
Date2 <- "2020-5-21"
Date3 <- "2020/5/21"
Date4 <- "20/05/21"
Date5 <- "20200521"
Date6 <- "2020 05 21"
Date7 <- "2020.05.21"
```
```{r datatype-date-14}
as.Date(Date1)
as.Date(Date2)
as.Date(Date3)
as.Date(Date4, "%y/%m/%d")
as.Date(Date5, "%Y%m%d")
as.Date(Date6, "%Y %m %d")
as.Date(Date7, "%Y.%m.%d")
```
`Date1`、`Date2`、`Date3`のようなベクトルの場合、`as.Date()`のみでDate型に変換できます。つまり、日付が数字のみで構成され、年が4桁となっており、年月日が`-`または`/`で区切られている場合はこれでだけで十分です。しかし、年が2桁になっていたり、その他の記号が使われたり、区切られていない場合は、`fotmat =`引数を指定する必要があります。たとえば`Date4`は年が2桁となっているます。2桁の年は`%y`と表記します。この表記法の一部を以下の表で紹介します。
| 表記 | 説明 | 例 |
|------|-----------|----------|
| `%y` | 年 (2桁) | 20 |
| `%Y` | 年 (4桁) | 2020 |
| `%m` | 月 (数字) | 5, 10 |
| `%b` | 月 (文字) | Jan |
| `%B` | 月 (文字) | January |
| `%d` | 日 | 5, 05, 13|
他にも様々な表記法がありますが、詳細は`?strptime`で確認してみてください。
他にも、日本では使わない表記法ですが、月を英語で表記したり、日月年の順で表記する場合があります。後者は`format = `引数の順番を変えるだけで問題有りませんが、問題は前者です。そこで使うのが`%b`または`%B`です。`%b`は3文字の月表記で、`%B`はフルネームです。
```{r datatype-date-15}
as.Date("21may2020", format = "%d%b%Y")
as.Date("May/21/2020", format = "%b/%d/%Y")
```
うまくいかないですね。これはシステムの時間ロケールが日本になっているのが原因です。ロケール設定は`Sys.getlocale()`で確認できます。
```{r datatype-date-16}
Sys.getlocale(category = "LC_TIME")
```
これを`Sys.setlocale()`を使って、`"C"`に変更します。
```{r datatype-date-17}
Sys.setlocale(category = "LC_TIME", locale = "C")
```
それではもう一回やってみましょう。
```{r datatype-date-18}
as.Date("21may2020", format = "%d%b%Y")
as.Date("May/21/2020", format = "%b/%d/%Y")
```
うまく動くことが確認できました。念の為に、ロケールを戻しておきます。
```{r datatype-date-19}
Sys.setlocale(category = "LC_TIME", locale = "ja_JP.UTF-8")
```
### POSIXct、POSIXlt型について
POSIXct、POSIXlt型は日付だけでなく時間の情報も含むデータ型です。これらは`as.POSIXct()`、`as.POSIXlt()`関数で作成することができます。どちらも見た目は同じデータ型ですが、内部構造がことなります[^posix]。詳細は`?as.POSIXct`または`?as.POSIXlt`を参照してください。
[^posix]: POSIXct型は基準日 (1970年1月1日 00時00分00秒 = UNIX時間)からの符号付き経過秒数であり、POSIXlt型は日付、時間などがそれぞれ数字として格納されています。可読性の観点からはPOSIXlt型が優れていますが、データ処理の観点から見るとPOSIXct型の方が優れていると言われます。
## NA {#sec-type-na}
NAは欠損値と呼ばれます。これは本来は値があるはずなのがなんらかの理由で欠損していることを意味します。 @tbl-datatype-na-1 の例を考えてみましょう。
```{r tbl-datatype-na-1}
#| echo: false
#| tbl-cap: "4人の支持政党"
data.frame(
ID = 1:4,
Name = c("Yanai", "Song", "Shigemura", "Tani"),
PID = c("ない", "ある", "ある", "ない"),
Party = c(NA, "ラーメン大好き党", "鹿児島第一党", NA)
) %>%
kable(col.names = c("ID", "名前", "支持政党の有無", "支持政党"),
booktabs = TRUE,
align = "crrr") %>%
kable_styling(bootstrap_options = "striped",
full_width = FALSE,
position = "center",
latex_options = "hold_position")
```
3列目で支持政党があるケースのみ、4列目に値があります。YanaiとTaniの場合、支持する政党がないため、政治政党名が欠損しています。実際、多くのデータには欠損値が含まれています。世論調査データの場合はもっと多いです。理由としては「Q2で"はい"を選んだ場合のみQ3に進み、それ以外はQ4へ飛ばす」のようなのもありますが、単に回答を拒否した場合もあります。
まずは欠損値が含まれたベクトル`na_vec1`を作ってみましょう。
```{r datatype-na-2}
na_vec1 <- c(1, NA, 3, NA, 5, 6)
na_vec1
```
つづいて、データ型を確認してみましょう。
```{r datatype-na-3}
class(na_vec1)
```
NAが含まれていてもデータ型はnumericのままです。これは「一応、欠損しているが、ここに何らかの値が割り当てられるとしたらそれはnumeric型だろう」とRが判断しているからです。ある要素がNAか否かを判定するには`is.na()`関数を使います。
```{r datatype-na-4}
is.na(na_vec1)
```
2番目と4番目の要素が欠損していることが分かります。
欠損値も要素の一つとしてカウントされるため、ベクトルの長さは6になります。ベクトルの長さは`length()`関数で確認できます。
```{r datatype-na-5}
length(na_vec1)
```
**欠損値の取り扱い**
欠損値を含むデータの処理方法はやや特殊です。まず、`na_vec1`の要素全てに1を足してみましょう。
```{r datatype-na-6}
na_vec1 + 1
```
この場合、欠損値の箇所には1が足されず、それ以外の要素のみに1を足した結果が返ってきます。これは直感的に考えると自然です。問題になるのは欠損値が含まれるベクトルを関数に入れた場合です。たとえば、numeric型ベクトル内の要素の総和を求めるには`sum()`関数を使います。`sum(c(1, 3, 5))`を入力すると9が返されます。`na_vec1`は欠損していない要素が1, 3, 5, 6であるため、総和は15のはずです。確認してみましょう。
```{r datatype-na-7}
sum(na_vec1)
```
このように欠損値を含むベクトルの総和はNAとなります。もし、欠損値を除いた要素の総和を求めるには、まずベクトルから欠損値を除去する必要があります。そのためには`is.na()`関数を使って`na_vec1`の要素を抽出します。ただし、`is.na()`を使うと、欠損値であるところが`TRUE`になるため、これを反転する必要があります。この場合は`!is.na()`関数を使います。それでは`is.na()`と`!is.na()`を使って要素を抽出してみましょう。
```{r datatype-na-8}
na_vec1[is.na(na_vec1)]
na_vec1[!is.na(na_vec1)]
```
`!is.na()`を使うことで欠損値を除いた要素のみを取り出すことができました。これなら`sum()`関数も使えるでしょう。
```{r datatype-na-9}
sum(na_vec1[!is.na(na_vec1)])
```
これで欠損値を除いた要素の総和を求めることができました。ただし、一部の関数には欠損値を自動的に除去するオプションを持つ場合があります。`sum()`関数のその一部であり、`na.rm = TRUE`オプションを付けると、欠損値を除いた総和を返します。
```{r datatype-na-10}
sum(na_vec1, na.rm = TRUE)
```
**欠損値の使い方**
主に欠損値を扱うのは入手したデータに含まれる欠損値に対してですが、NAをこちらから生成することもあります。それは空ベクトルを用意する時です。第[-@sec-programming]章では関数の作り方について解説します。関数内で何らかの処理を行い、その結果を返すことになりますが、その結果を格納するベクトルを事前に作っておくこともできます。こちらの方がメモリの観点からは効率的です。以下は第[-@sec-programming]章を読んでから読んでも構いません。
もし、長さ10のベクトル`result_vec1`を返すとします。ベクトルの要素として1から10の数字が入るとします。一つ目の方法としてはまず、`result_vec1`に1を代入し、次は`c()`を使って要素を一つずつ足して行く手順です。
```{r datatype-na-11}
#| eval: false
result_vec1 <- 1
result_vec1 <- c(result_vec1, 2)
result_vec1 <- c(result_vec1, 3)
result_vec1 <- c(result_vec1, 4)
result_vec1 <- c(result_vec1, 5)
result_vec1 <- c(result_vec1, 6)
result_vec1 <- c(result_vec1, 7)
result_vec1 <- c(result_vec1, 8)
result_vec1 <- c(result_vec1, 9)
result_vec1 <- c(result_vec1, 10)
```
二つ目の方法はまず、10個のNAが格納されたベクトル`Reuslt.Vec2`を作っておいて、その中に要素を置換してく方法です。
```{r datatype-na-12}
#| eval: false
result_vec2 <- rep(NA, 10)
result_vec2[1] <- 1
result_vec2[2] <- 2
result_vec2[3] <- 3
result_vec2[4] <- 4
result_vec2[5] <- 5
result_vec2[6] <- 6
result_vec2[7] <- 7
result_vec2[8] <- 8
result_vec2[9] <- 9
result_vec2[10] <- 10
```
以上の手順を第[-@sec-programming]章で紹介する`for()`を使って反復処理するとしたら、以下のようなコードになります。
```{r datatype-na-13}
# 方法1
result_vec1 <- 1
for (i in 2:10) {
result_vec1 <- c(result_vec1, i)
}
# 方法2
result_vec2 <- rep(NA, 10)
for (i in 1:10) {
result_vec2[i] <- i
}
```
結果を確認してみましょう。
```{r datatype-na-14}
result_vec1
result_vec2
```
コードの書き方は異なりますが、どれも結果は同じです。また、どちらが早いかというと、これくらいの計算ならどの方法でも同じです。ただし、より大規模の反復作業を行う場合、後者の方が時間が節約でき、コードの可読性も高いです。
## NULL {#sec-type-null}
NULLは「存在しない」、空っぽであることを意味します。先ほどのNAはデータは存在するはずなのに、何らかの理由で値が存在しない、または割り当てられていないことを意味しますが、NULLは「存在しません」。したがって、NULLが含まれたベクトルを作成しても表示されません。NULLが含まれた`null_vec1`を作ってみましょう。
```{r datatype-null-1}
null_vec1 <- c(1, 3, NULL, 5, 10)
null_vec1
```
3番目の要素であるNULLは表示されません。ということはNAと違って、データの長さも5ではなく4でしょう。確認してみます。
```{r datatype-null-2}
length(null_vec1)
```
この`null_vec1`のデータ型は何でしょう。
```{r datatype-null-3}
class(null_vec1)
```
`is.null()`関数もありますが、どうでしょうか。
```{r datatype-null-4}
is.null(null_vec1)
```
NULLは存在しないことを意味するため、`null_vec1`は要素が4のnumeric型ベクトルです。`is.null()`でNULLが判定できるのは`is.null(NULL)`のようなケースです。
@fig-datatype-null-5 はNA型とNULL型の違いについてまとめたものです。
![NAとNULLの違い](Figs/Datatype/NA_NULL.png){#fig-datatype-null-5 width="800px" fig-align="center"}
このNULLはいつ使うのでしょうか。実際、使う機会はあまりありません。強いて言えば、空っぽのリストを作成する際に使うケースがあります。リストについては第[-@sec-datastructure]章で説明します。以下の例は第[-@sec-datastructure]章を読み終わってから目を通して下さい。
```{r datatype-null-6}
null_list1 <- list(Room1 = 1:3,
Room2 = c("Yuki", "Jaehyun", "Hadley"),
Room3 = NULL)
null_list1
```
このように予めリストの要素は作っておきたいが、とりあえず空けておく際に使います。続く分析の段階で`null_list1[["Room3"]]`に何かを格納したりすることに使えるでしょう。ちなみにこの場合は`is.null()`が使用可能です。
```{r datatype-null-7}
is.null(null_list1[["Room3"]])
```
## NaN {#sec-type-nan}
NaNはnumeric型、中でもdouble型の一種ですが、これは計算できない値を意味します。つまり、NaN値を直接入力することはめったにありませんが、計算の結果として`NaN`が返されるケースがあります。代表的な例が0を0で割った場合です。実際、0を0で割ることはできません。ここでは0を0で割った値を含む`nan_vec1`作ってみましょう。
```{r datatype-nan-1}
nan_vec1 <- c(2/5, 0/12, 0/0)
nan_vec1
class(nan_vec1)
```
先ほどせつめいしましたように、NaNはnumeric型の一部ですので、データ型としてはnumeircになります。ある値がNaNか否かを判定するには`is.nan()`関数を使います。
```{r datatype-nan-2}
is.nan(nan_vec1)
```
## Inf {#sec-type-inf}
Infもまたnumeric型、中でもdouble型の一部ですが、これは無限大を意味します。Infも通常、自分から作成するケースはあまりなく、結果として帰ってくる場合があります。一つの例が0以外の数値を0で割った場合です。それではなんらかの数値を0を割った値が含まれるベクトル`inf_vec1`を作ってみましょう。
```{r datatype-inf-1}
inf_vec1 <- c(28/95, 3/0, -12/0, 0/0)
inf_vec1
class(inf_vec1)
```
正の値を0で割ったら`Inf`が負の値を0で割ったら`-Inf`が返ってきます。これは正の無限大、負の無限大を意味します。データ型はNaNと同様、numeric型ですが、`is.infinite()`を使うと、無限大か否かが判定できます。
```{r datatype-inf-2}
is.infinite(inf_vec1)
```