-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathChapter3_dt_base.qmd
676 lines (443 loc) · 24.2 KB
/
Chapter3_dt_base.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
---
title: "chapter 3. data.table"
author: "Rchemist"
format: html
editor: source
---
## 1. data.table 소개
`data.table`은 R의 `data.frame`을 토대로 만들어진 패키지입니다. 그렇기 때문에 대괄호를 쓰는 등 문법은 대체로 `data.frame`과 비슷합니다. 그러나 `data.table`은 기존의 `data.frame`보다 매우 빠른 속도를 자랑합니다.
`data.table`의 장점은 다음과 같습니다.
- **매우 빠른 속도**
`data.table`은 기본 `data.frame` 구조보다 훨씬 빠르게 데이터를 연산합니다.
```{r}
library(data.table)
library(dplyr)
library(microbenchmark)
data('storms')
df_test <- function(){
aggregate(x=storms[,c('wind','pressure')],
by = list(storms$name,storms$year, storms$month,storms$day),
FUN = mean)
}
dplyr_test <- function(){
storms %>%
group_by(name, year, month, day) %>%
summarise(wind=mean(wind), pressure=mean(pressure),
)
}
storm_dt <- as.data.table(storms)
dt_test <- function(){
storm_dt[,.(wind=mean(wind), pressure=mean(pressure)), by=.(name, year,month,day)]
}
microbenchmark(df_test(),
dplyr_test(),
dt_test(),
times=10)
```
위는 각각 `data.frame`, `dplyr`, `data.table`로 동일한 결과를 불러오도록 실행했ㅇ르 때 걸린 시간입니다. `data.table`의 결과가 최소 10배는 더욱 빠른 것을 알 수 있습니다.
- **효율적인 메모리 처리**
data.table은 다른 데이터 패키지보다 효율적으로 데이터연산을 처리합니다. 그렇기 때문에 메모리 사용에 있어서도 더 적은 양으로 더 빠르게 계산을 진행합니다.
- **낮은 패키지 의존성**
패키지 의존성이라는 것은 특정 패키지를 불러오기 위해 또다른 패키지를 불러오는 것입니다. 우리가 배울 `data.table`은 R 사용자들에게 자주 활용되는 `tidyverse` 계열의 패키지보다 의존성이 훨씬 낮습니다. 그렇기 때문에 번거롭게 하나의 패키지를 사용하기 위해 다른 패키지들을 설치해줄 필요가 없습니다.
------------------------------------------------------------------------
## 2. data.table 함수
`data.table` 패키지에는 data.table에서만 사용할 수 있는 고유의 함수들이 존재합니다. 이번 장에서는 data.table문법을 배우기에 앞서, 데이터를 읽고 저장하는 함수, 이름 변경 등 기초적인 함수들을 우선 배워봅시다.
- `fread()`: f(ast) + read의 의미입니다. 말그대로 "빠르게 데이터를 불러오는 것"(read)을 의미합니다. `.csv`, `.txt` 등의 확장자 이름을 가진 파일들을 불러올 수 있습니다.
```{r}
dt <- fread('https://raw.githubusercontent.com/rchemist0123/datasets/main/NHANES.csv')
```
실제로 `fread()`가 지난 시간에 배운 `read.csv()`보다 얼마나 빠른지 확인해봅시다.
```{r}
#| message: false
library(microbenchmark)
microbenchmark(
read.csv = read.csv('https://raw.githubusercontent.com/rchemist0123/datasets/main/NHANES.csv'),
fread = fread('https://raw.githubusercontent.com/rchemist0123/datasets/main/NHANES.csv'),
times=10
)
```
확인 결과, `fread()`가 `read.csv()`에 비해 약 4~5배는 빠른 것을 알 수 있습니다.
`fread`를 통해 불러온 파일의 `class`는 `data.table`입니다.
```{r}
class(dt)
```
앞서 언급한 것처럼, data.table에는 data.frame을 상속받아 만들었기 때문에, data.frame 역시 같이 출력되는 것을 알 수 있습니다.
`data.table`을 출력했을 때, `data.frame`과 다른 점은 크게 두 가지가 있습니다. `data.table`은 우선 모든 column에 대해 첫 5개, 마지막 5개의 행을 출력합니다.
또한 행의 번호에 `:`가 붙어 출력됩니다.
- `fwrite()`: f(ast) + write 입니다. 말그대로 "빠르게 데이터를 저장(write)"합니다. 현재의 작업공간에 저장되어 있는 object를 `.csv`, `.txt` 등의 확장자 파일로 저장할 수 있습니다.
```{r}
#| eval: false
fwrite(dt,'temp.csv')
```
- `setnames()`: 열(column) 이름을 사용자가 알아보기 쉽게끔 변경해야 할 때가 있습니다. `setnames()`는 column의 이름을 변경하는 함수입니다. column의 이름을 하나만 바꾸고 싶은 경우에는 문자열 하나만 넣어주면 되고, 여러 개의 column 이름을 동시에 변경할 때는 문자열 벡터를 넣어주면 됩니다.
```{r}
# 하나의 column 이름을 변경할 때
setnames(dt, old='Gender', new='gender')
# 여러 개의 column 이름을 동시에 변경할 때
setnames(dt,
old = c('Age','Race1','Education'),# 바꿔줄 기존의 column 이름
new = c('age','race','education') # 새로운 column 이름
)
```
- `data.frame` 등을 `data.table`로 변경하기
새롭게 파일을 불러오는 것 뿐만 아니라 기존의 data.frame을 data.table 형태로 변경해줄 수 있습니다.
- `setDT()` : 영구적으로 data.table 형태로 **저장**합니다.
```{r}
setDT()
```
- `as.data.table()` : 일시적으로 data.table 형태로 **출력**합니다.
```{r}
as.data.table(iris)
```
::: callout-tip
data.table 출력 시 열(column) 데이터 유형 출력하기
data.table과 더불어 R에서 가장 많이 활용되는 데이터 핸들링 패키지는 dplyr입니다. dplyr를 활용해 데이터를 출력했을 때, 열별로 어떤 유형인지 출력되는 것을 알 수 있습니다.
```{r}
library(dplyr)
head(starwars)
```
`data.table` 역시 간단하게 옵션만 설정해준다면 각 열별 데이터 유형을 확인할 수 있습니다.
```{r}
options(datatable.print.class = TRUE) # 데이터 유형 출력 설정
head(df)
```
:::
------------------------------------------------------------------------
## 3. i: data.table 행 다루기
통상적으로 data.table에서 행(row)을 다루는 부분을 `i` 라고 부릅니다.
data.table에서 행을 다룬다는 것은 [특정한 조건을 만족하는 행들을 추출]{.underline}(filtering)한다는 것과 같습니다.
자, 이제 data.table에서 행을 다루는 방법을 살펴보겠습니다.
### 1) 논리 연산자를 이용한 행 추출
Chapter 2의 data.frame 행 부분에서도 배웠지만, 기본적으로 i에서는 논리 연산자를 이용해 행을 선택합니다. 논리 연산자의 조건을 만족하는 행들, 즉 논리 연산자의 실행 결과가 `TRUE` 인 행들만 추출합니다.
```{r}
dt[age>=30,]
```
data.table에서는 data.frame과 다르게, 행을 추출할 때, `,`를 붙이지 않아도 됩니다.
```{r}
dt[age<30]
```
`&`나 `|`를 붙여주면, 여러 조건을 사용하여 원하는 행들을 추출할 수 있습니다.
```{r}
dt[gender=='male' & age>=45] # 남성이고(AND) 45세 이상
```
```{r}
dt[gender=='female' | age>=50] #여성 또는(OR) 50세 이상
```
::: callout-note
## data.table과 data.frame의 차이점
`data.frame`과 달리 `data.table` 문법에서는, 대괄호 안에서 column을 `df$var` 양식으로 사용하지 않아도 됩니다. 그냥 column의 이름만 사용하면 됩니다.
```{r}
# data.frame 방식
dt[dt$Gender == 'male' & dt$age >= 60]
# data.table 방식
dt[Gender == 'male' & age >= 60]
```
:::
### 2) 파이프 연산자를 이용한 행 추출
`data.table`에서는 논리 연산자 뿐만 아니라 파이프 연산자(`%%`가 붙은 연산자)를 이용하여, 조건을 충족시키는 행을 선택할 수 있습니다. 파이프 연산자 역시 조건을 만족하는 경우인 `TRUE` 에 해당하는 값들만 선택합니다.
`data.table`에 포함된 대표적인 파이프 연산자는 다음과 같습니다.
- `A %in% B`: A의 값 중에서 B에 포함되어 있는 값을 추출합니다. 이 때 B에는 벡터 형태의 여러 개의 값을 선택할 수 있습니다.
```{r}
dt[race %in% c('Black','White')]
```
- `A %like% B`: A의 값 중에서 B와 비슷한지 값을 추출합니다. 이 때 B에는 보통 문자열(`character`)이 옵니다. 비슷한 값을 확인하는 것이기 때문에 정규표현식을 사용해 찾을 수 있습니다.
```{r}
dt[MaritalStatus %like% 'Married']
```
`%in%`은 `c()`를 이용해 여러 개의 값을 추출할 수 있지만, `%like%`는 c()를 이용할 수 없습니다.
```{r}
dt[MaritalStatus %like% c('Married','Divorced')]
```
가장 첫번째의 패턴('Married')만을 이용한다는 경고메시지가 나옵니다.
두 개 이상의 값을 `%like%`로 찾고자 한다면 `|`을 이용해야 합니다.
```{r}
dt[MaritalStatus %like% 'Married|Divorced']
```
- `A %between% B`: A의 값 중에서 B 사이에 있는지 확인합니다. 이 때 B는 `c(0,10)`과 같은 범위로 지정합니다.
```{r}
dt[BMI %between% c(20,25)]
```
::: callout-tip
## 파이프 연산자 대신 함수 이용하기
위에서 소개해드린 `%%`을 이용한 파이프 연산자 말고도 `data.table` 패키지 내의 함수를 이용할 수도 있습니다.
`%between%`은 `between()`와 같고,
`%like%` 는 `like()`와 같습니다.
```{r}
dt[like(race,'ite')]
dt[between(BMI, c(0,25))]
```
:::
### 3) 함수를 이용한 행 추출
논리 연산자나 파이프 연산자 뿐만 아니라 `TRUE`/`FALSE`를 반환하는 다른 함수들도 행을 선택하는 데 활용할 수 있습니다. 가장 대표적인 함수가 바로 `is.na()` 입니다.
```{r}
dt[is.na(Testosterone)]
```
`NA`가 아닌 데이터를 출력하는 방법은 `!is.na()` 입니다. Chapter 2의 논리 연산자 부분에서 `NOT`을 의미하는 기호는 `!`라고 배웠습니다.
```{r}
dt[!is.na(SleepHrsNight)]
```
### 4) 행의 정렬 order
행의 정렬 역시 `i` 부분에서 담당합니다. 특정한 column을 기준으로 데이터를 오름차순 또는 내림차순 정렬을 할 때 사용할 수 있습니다.
data.table에서 특정 column을 기준으로 데이터를 정렬하는 방법은 두 가지가 있습니다.
- `order()`: `order()`는 정렬한 값의 **출력**만 합니다.
```{r}
dt[order(BMI)] %>% head()
```
만약 내림차순으로 정렬하고 싶은 경우는 변수 앞에 -를 붙여주면 됩니다.
```{r}
dt[order(-BMI)] %>% head()
```
- `setorder()`: `setorder()`는 데이터를 정렬하여 data.table에 **저장**합니다. `order`와는 다르게 정렬된 결과를 출력하지는 않습니다.
```{r}
setorder(dt, # 정렬할 데이터
age # 기준이 되는 변수
)
```
::: callout-tip
## set이 들어가는 data.table 함수
`data.table`에서 `set`이 붙는 함수는 어떤 값을 출력없이 **저장**하는 함수입니다.
```{r}
#| eval: false
setnames() # column의 이름 변겅
setorder() # row 정렬
setDT() # data.table로 저장
```
:::
::: callout-note
## 얕은 복사와 깊은 복사
얕은 복사(shallow copy)란 메모리 상 같은 주소를 참조하여, 복사를 했다고 하더라도 영향을 받는 객체입니다.
```{r}
dt2 = dt
address(dt)
address(dt2)
```
예를 들어, `dt`를 복사해 `dt2`를 만들었고 `dt2`에서 새로운 열을 생성해보겠습니다.
```{r}
dt2[, new_column := 1]
dt2[,.(new_column)] |> head()
dt[,.(new_column)] |> head()
```
분명 `dt2`에 새로운 열을 만들었는데, `dt`에도 똑같은 열이 생성되었습니다. 이는 `dt2`가 `dt`의 얇은 복사를 통해 만들어진 객체이기 때문입니다. 즉 두 객체는 서로 같은 메모리 주소를 공유하고 있기 때문에, 서로 영향을 받게 됩니다.
반면 깊은 복사(deep copy)란, 두 객체가 메모리 내에 다른 주소를 갖게 되어, 서로 영향을 받지 않게 됩니다.
```{r}
dt3 = copy(dt)
address(dt)
address(dt3)
```
이는 dt3에 새로운 열을 생성해도, dt에는 아무런 영향을 미치지 못한다는 것을 의미합니다.
```{r}
dt3[, new_column2 := 1]
dt3[,.(new_column2)] |> head()
dt[,.(new_column2)] |> head()
```
:::
------------------------------------------------------------------------
## 4. j: data.table 열 다루기
`j` 부분은 데이터의 열 (column)을 다루는 부분을 의미합니다. `j` 부분을 통해 원하는 열들을 선택하거나, 특정 열을 계산할 수 있습니다. 또한 새로운 변수를 생성하거나 기존의 변수를 수정 또는 삭제할 수 있습니다.
`j` 를 활용하기 위해선 `dt[,j]` 처럼 앞에 `,`를 항상 붙여줘야 합니다.
### 1) column 선택
`data.table`에서 열을 선택하는 방법은 다양합니다.
- `dt[,c('X','Y')]`
- `dt[,list(X,Y)]`
- `dt[,.(X,Y)]`
1번은 data.frame에서 열을 선택할 때와 동일합니다. 2,3번은 `data.table`에서만 가능한 방법입니다.
2번과 3번 방법은 열 이름을 다 입력할 필요 없이, 자동완성 기능을 통해 필요한 열을 찾을 수 있습니다.
여기서 `list()`와 `.()`은 동일한 기능입니다. 동일한 기능이라면 더 적은 코드를 입력하는 후자가 더 낫겠죠?
```{r}
dt[,.(gender,age)]
```
문자 벡터를 이용하여 원하는 열을 선택할 수도 있습니다. 이 때는 `..`기호를 이용해야 합니다.
```{r}
target <- c('gender','age','race')
dt[,..target]
```
### 2) column 계산
column을 계산하는 것 역시 data.table의 `j` 부분에서 담당합니다. 예를 들어, 특정 column의 평균을 계산하거나 표준편차를 계산하는 경우가 있겠죠. 만약 하나의 column만 계산하는 경우 data.table의 `[]` 안에서 함수를 이용하면 됩니다.
```{r}
dt[,mean(BMI,na.rm=T)]
```
만약 여러 개의 column에 대해 계산하는 경우 또는 계산하는 값을 `data.table` 형태로 출력하고 싶은 경우, column 선택에서 배웠던 `.()`를 활용하면 됩니다.
또한 `.()`를 이용하게 되면, 계산하는 값에 이름을 부여할 수 있습니다.
```{r}
dt[,.(mean_BMI = mean(BMI, na.rm=T),
sd_BMI = sd(BMI,na.rm=T))]
```
### 3) column 생성 및 변경
`data.table`에는 특수한 기호가 있습니다. 바로 `:=` 입니다. walrus (바다코끼리) 연산자라고도 불리기도 합니다. 바다코끼리의 어금니를 닮아서 붙여진 이름 같습니다.
![바다코끼리(Walrus)의 어금니가 `:=` 와 닮았습니다.](images/walrus.jpg){fig-align="center" width="400"}
`data.table`을 사용한다면 `:=` 연산자를 활용하는 것이 굉장히 중요합니다. 새로운 열을 추가하거나, 기존의 열을 변경할 때, 삭제할 때 활용되는 연산자 입니다.
또한 `:=`는 `set`이 들어간 함수처럼 결과를 출력하지 않고, 데이터를 변경시켜 저장합니다.
#### a. column의 생성
새로운 열을 생성하기 위해서는 `dt[,column_name := value]` 형식으로 코드를 작성합니다. `:=`의 왼쪽에는 새로운 열의 이름이, 오른쪽에는 데이터를 입력합니다.
```{r}
dt[,age_group := paste0((age %/% 10) * 10,'대')]
dt[,table(age_group)]
```
`age` 를 이용하여 `age_group`(연령대) column을 추가하였습니다.
만약 여러 개의 column을 동시에 추가하고 싶다면, 백틱(``` `` ```) 또는 따옴표(`''`)와 함께 `:=`를 사용해야 합니다.
이 때 `()`안의 등호들은 `:=`가 아니라 그냥 등호(`=`)를 사용해야 합니다.
```{r}
dt[,`:=`(
age2 = age/5,
age3 = age/10
)]
```
#### b. column의 변경
column을 변경한다는 것은 기존의 값들을 다르게 바꿔준다는 것입니다. 예를 들면 1과 2로 이루어진 column을 'no'와 'yes'로 변경해주는 것처럼요.
column을 변경하는 것 역시 생성과 마찬가지로 `dt[,column_name := value]` 형식으로 코드를 작성하면 됩니다. `column_name`에는 바꿔줄 column의 이름이, `value`에는 새롭게 넣어줄 데이터를 입력합니다.
```{r}
dt[,gender := ifelse(gender=='male','m','f')]
```
위의 코드처럼 `:=`를 이용해 `male`, `female`로 되어있는 `Gender` 를 각각 `m`과 `f`로 변경할 수 있습니다.
한편 여러 개의 column을 생성하거나 변경하는 경우, 새로운 column을 하나의 `[]`안에서 바로 사용할 수 없습니다.
```{r}
dt[,`:=`(
A2=age,
A3=A2
)]
```
위 코드는 A2와 A3 column을 동시에 생성하는 코드입니다. 그러나 A2가 아직 만들어지지 않았기 때문에, A3 column은 생성될 수 없습니다.
따라서,
```{r}
dt[,A2:=age][,A3:=A2]
```
위의 코드처럼 `[]`을 여러 번 붙여 순차적으로 column을 생성 또는 변경해주어야 합니다. 이를 **chaining**이라고 합니다.
#### c. column의 삭제
data 내에 존재하는 column을 삭제할 때는 `dt[,column_name := NULL]` 형식을 사용합니다. 삭제하려는 하는 `column_name`만 선택하면 되겠죠.
```{r}
dt[,column_name := NULL]
```
`:=` 연산자를 활용한 코드를 실행하게 되면 해당 데이터가 저장이 될 뿐, 출력이 되진 않습니다 (`set`함수와 같은 역할). 변경한 column을 확인하기 위해선 대괄호를 한 번 더 붙여줍니다.
```{r}
dt[,Var1 := var][]
dt[,column_name := NULL][]
```
------------------------------------------------------------------------
## 5. by: 그룹 별 분석
`by`: 특정 column에 따라 `j` 를 계산합니다. 예를 들어 성별에 따른 나이의 평균을 구하는 경우, 따라서 [`by`를 사용하기 위해서는 `j` 부분이 존재해야]{.underline} 합니다.
```{r}
dt[,.(mean_age = mean(age)),by=MaritalStatus]
```
`keyby`: by와 마찬가지로 특정 그룹에 따라 계산합니다. 하지만 `by`와 다르게 그룹으로 선택된 column을 기준으로 정렬하여 결과값을 출력합니다.
```{r}
dt[,.(mean_age = mean(age)),keyby=MaritalStatus]
```
혼인상태에 따른 나이의 평균 값이 혼인상태의 이름으로 오름차순 정렬이 되어 출력된 것을 확인할 수 있습니다.
------------------------------------------------------------------------
## 6. 이외 유용한 data.table 전용 함수
- `fifelse()`: fast ifelse입니다. `ifelse()`보다 더 빠르게 작업을 수행합니다.기본 `ifelse()`와 다르게 yes와 no 자리에 오는 인자들의 유형(type)이 반드시 동일해야 합니다.
```{r}
dt[,SEX := fifelse(gender=='m','남성','여성')]
dt[,Temp := fifelse(age<10,NA,age)] # error 발생.
```
- `fcase()` : fast + case when입니다. case when은 `ifelse()`처럼 조건을 사용해 데이터를 조작하지만, 반복적으로 `ifelse()` 함수를 계속해서 사용할 필요가 없기 때문에 더 적은 코드를 사용합니다.
```{r}
dt[,age_group2 := fcase(
age<20,'10대',
age<30,'20대',
age<40,'30대',
age<50,'40대',
age<60,'50대'
)]
dt[,table(age_group2)]
```
조건에 해당되지 않는 나머지 값들은 자동으로 `NA` 처리 됩니다. `ifelse()`처럼 나머지 값들에 값을 주고 싶다면 `default` 인자를 활용하면 됩니다.
```{r}
dt[,age_group2 := fcase(
age<20,'10대',
age<30,'20대',
age<40,'30대',
age<50,'40대',
age<60,'50대',
default='60대 이상'
)]
dt[,table(age_group2)]
```
- `uniqueN()` : 특정 열의 고유한 데이터 개수를 확인하는 데 사용하는 함수입니다.
```{r}
dt[,BMI2:=fifelse(is.na(BMI),'donno', fifelse(BMI>=30,'obese','normal'))][,uniqueN(BMI2)]
```
- `nafill()`: 열 단위로 결측치(`NA`)를 채워넣습니다. 기존의 `fifelse()`를 사용해서, NA인 조건을 찾지 않아도 됩니다. `type` 인자는 `const`, `locf`, `nocb` 등을 선택할 수 있습니다.
- `const`: 특정 상수(constant)를 넣는 것입니다. 평균, 중앙값과 같은 특정 값으로 모든 NA를 채워넣습니다. `type=const`일 경우는 `fill`인자에는 채워넣을 값을 입력합니다.
- `locf`: Last Observation Carried Forward: `NA`가 아닌 마지막 값으로 다음 값을 `NA`로 채워넣습니다. 예를 들어 6행이 `NA`이고 5행이 `NA`가 아니라면 5행의 값으로 6행의 `NA`를 채워넣습니다.
- `nocb`: Next Observation Carried Backward: `NA`가 아닌 첫 값으로 이전 값을 `NA` 로 채워넣습니다. 예를 들어 5행이 `NA`이고 6행이 `NA`가 아니라면 6행의 값으로 5행의 `NA`를 채워넣습니다.
```{r}
dt[, AlcoholDay2 := nafill(AlcoholDay, type='const', fill = mean(AlcoholDay, na.rm=T))]
```
------------------------------------------------------------------------
## 7. 셀프 과제
수업시간에 배운 내용을 토대로 `data.table` 문법을 활용하여 문제를 해결합니다.
[과제 데이터의 변수 설명서](https://docs.google.com/spreadsheets/d/1JTxiXAtXOVwnmOeIPW1ZYlRAwYmKKjXqtcMx0xTDkiw/edit#gid=1274720220)를 참고하여 문제를 풀어보세요. 😄
- 과제데이터: '`3주차_diabetes_big.csv'`
### 1) data.table 함수 익히기
1-1. `data.table` 의 함수를 이용하여 과제 데이터를 불러오세요.
```{r}
```
1-2. `data.table`의 함수를 이용하여 아래와 같이 column명을 변경해보세요.
```{r}
```
- `PhysHlth` $\rightarrow$ `physical_health`
- `MentHlth` $\rightarrow$ `mental_health`
- `GenHlth` $\rightarrow$ `general_health`
- `PhysActivity` $\rightarrow$ `physical_activity`
------------------------------------------------------------------------
### 2) i: data.table 행 다루기
2-1. `BMI`가 25 이상인 데이터의 첫 6행을 출력하세요.
```{r}
```
2-2. `Age`가 6 이상 이고 `Education`이 5 이상인 데이터의 첫 6행을 출력하세요.
```{r}
```
2-3. 정신건강(`mental_health`)이나 신체건강(`physical_health`)이 안 좋은 날이 10일 이상 20일 이하인 데이터의 마지막 6행을 출력하세요.
```{r}
```
2-4. 흡연자(`Smoker`=1)이고 고위험 음주(`HvyAlcoholConsump`=1)면서 신체활동을 하지 않는 `physical_activity`=0) 경우를 출력하세요.
```{r}
```
2-5. 과제 데이터를 `BMI` 가 낮은 순으로 정렬 (오름차순 정렬) 한 뒤, 첫 6행을 출력하세요.
```{r}
```
------------------------------------------------------------------------
### 3) j: data.table 열 다루기
3-1. `HeartDiseaseorAttack` 인 사람들의 `BMI` 평균을 계산하세요.
```{r}
```
3-2. 고위험 음주(`HvyAlcoholConsump` )인 사람들의 정신건강이 좋지 않은 날의 중앙값과 최솟값, 최댓값을 계산하세요.
```{r}
```
3-3. `BMI` column을 이용하여 비만 여부(`obesity`) column을 만드세요.
- 비만 기준: BMI 25 이상.
```{r}
```
3-4. 흡연자 (`Smoker`)이고 고위험 음주 (`HvyAlcoholConsump`)를 하면서 운동(`physical_activity`)는 하지 않는 사람을 분류하는 `bad_habit` column을 만드세요.
- `bad_habit`: 흡연자(Smoker=1), 고위험 음주(`HvyAlcoholConsump`=1), 신체활동 안함(`physical_activ`ity=0) 에 모두 해당하는 경우 1, 나머지 0
```{r}
```
3-5. 심장질환(`HeartDiseaseorAttack`) 또는 뇌졸중(`Stroke`) 이력이 있는 사람들을 분류하는 질병 이력(`history`) column을 만든 뒤, 심장질환, 뇌졸중, 질병이력 column을 선택하여 출력하세요.
```{r}
```
3-6. 신체 건강 (`physical_health`)과 정신 건강 (`mental_health`)이 좋지 않은 날의 합을 계산하여 건강상태를 분류 하는 `health_condition` column을 만든 뒤, `health_condition`이 `excellent` 이거나 `good` 인 사람들의 `bmi` 평균을 계산하세요.
- 5 미만 $\rightarrow$ excellent
- 5 이상 10 미만 $\rightarrow$ good
- 10 이상 20 미만 $\rightarrow$ normal
- 20 이상 30 미만 $\rightarrow$ bad
- 30 이상 $\rightarrow$ very bad
```{r}
```
------------------------------------------------------------------------
### 4) by: 그룹별 계산 문제
4-1. 당뇨 (`Diabetes_binary`)에 따른 `BMI` 평균을 계산하세요.
```{r}
```
4-2. 질병 이력 (`history`)에 따른 나이 (`Age`)의 최솟값과 최댓값을 계산하세요.
```{r}
```
4-3. 생활 습관 (`bad_habit`) 에 따른 비만 여부(`obesity`), 당뇨(`Diabetes_binary`), 질병 이력(`history`)의 합계를 각각 계산하세요. 만약 위의 column들을 numeric이 아닌 column으로 만들었다면 numeric으로 변환 후 계산하세요.
```{r}
```
4-4. 주관적 건강상태 (`general_health`) 별로 `BMI`의 중앙값을 계산한 뒤, 주관저 건강상태 별 오름차순 정렬한 결과를 출력하세요.
```{r}
```
4-5. `health_condition` 에 따른 비만 여부(`obesity`), 당뇨(`Diabetes_binary`), 질병 이력(`history`), 생활 습관 (`bad_habit`) 변수의 합을 더한 `health_sum` column을 만드세요.
```{r}
```