시작하기 전에
본 자료는 빠른 속도와 메모리 효율성에 강점이 있는 data.table 패키지에 관해 기본 개념과 문법, 함수들을 예제를 통해 다루어 볼 것이다.
1. 개념 소개
data.table 패키지는 대용량의 데이터를 분산 처리 시스템 없이 처리할 수 있는 방법으로, 데이터 프레임을 대신하여 더 빠르고 편리하게 사용할 수 있는 데이터 타입이다.
장점- 상대적으로 메모리 요구량이 적고, 속도가 매우 빠르다.
단점- 다소 난해한 문법으로 널리 사용되지는 못하고 있다.
2. 기본 문법
data.table 의 기본 문법은 DT[i, j, by] 형태이다.
- i는 행을 선택힌다.
- j는 열을 선택하거나 열 또는 테이블 전체에 함수를 적용한다.
- by는 집단을 나눈다. j에서 지정한 열과 함수를 by 그룹 별로 수행한다.
- 뒤에 [order]를 붙여 오름차순이나 내림차순으로 정렬할 수 있다.
Load & save data: fread
& fwrite
fread
함수로 빠르게 csv 파일을 읽어와서 data.table
자료로 만들 수 있고, fwrite
함수로 csv 파일을 쓸 수 있다. fread
와 fwrite
는 이름답게 매우 빠르며 Base R의 함수보다 40배 더 빠른 속도를 자랑한다. 파일을 읽어와서 data.table
자료로 만들 때, 로컬 file path를 입력하거나 http:// 로 시작하는 url을 입력하는 방법을 사용한다. fread
로 파일을 읽으면 그 class는 data.frame
에 data.table
이 추가되며 문법이 원래의 data.frame
과 달라지는 점을 유의하자.
fread
를 통해 데이터를 불러와 data.table 형태로 만들어보자. 데이터는 09-15년 공단 건강검진 데이터에서 실습용으로 32명을 뽑은 자료이며, 자세한 내용은 “data/2교시 테이블 세부 레이아웃 소개(최신자료).pdf” 를 참고하자.
Setup
## Setup
# install.packages("data.table")
# install.packages("curl")
library(data.table)
library(curl)
Using libcurl 8.6.0 with LibreSSL/3.3.6
Load file
# Load file
url <- "https://raw.githubusercontent.com/jinseob2kim/lecture-snuhlab/master/data/example_g1e.csv"
df <- read.table(url,header=T)
dt <- fread(url,header=T)
# Class
print(class(df))
[1] "data.frame"
[1] "data.table" "data.frame"
dt의 class에 data.table
이 추가된 것을 볼 수 있다.
# dt = data.table(df)
# df = data.frame(dt)
## See
dt
EXMD_BZ_YYYY <int> | RN_INDI <int> | HME_YYYYMM <int> | Q_PHX_DX_STK <int> | Q_PHX_DX_HTDZ <int> | Q_PHX_DX_HTN <int> | |
---|---|---|---|---|---|---|
2009 | 562083 | 200909 | 0 | 0 | 1 | |
2009 | 334536 | 200911 | 0 | 0 | 0 | |
2009 | 911867 | 200903 | 0 | 0 | 0 | |
2009 | 183321 | 200908 | NA | NA | NA | |
2009 | 942671 | 200909 | NA | NA | NA | |
2009 | 979358 | 200912 | NA | NA | NA | |
2009 | 554112 | 200911 | NA | NA | NA | |
2009 | 487160 | 200908 | NA | NA | NA | |
2009 | 793017 | 200906 | NA | NA | NA | |
2009 | 219397 | 200912 | 0 | 0 | 1 |
Save file
row operation
행을 선택하는 slice는 data.table
에서 DT[c(row1, row2, …),] 또는 DT[c(row1, row2, …)]의 형식으로 data.frame
과 동일하게 쓰인다.
dt[c(3,5)]
EXMD_BZ_YYYY <int> | RN_INDI <int> | HME_YYYYMM <int> | Q_PHX_DX_STK <int> | Q_PHX_DX_HTDZ <int> | Q_PHX_DX_HTN <int> | |
---|---|---|---|---|---|---|
2009 | 911867 | 200903 | 0 | 0 | 0 | |
2009 | 942671 | 200909 | NA | NA | NA |
특정한 조건을 만족하는 행을 선택하는 filter는 data.table
에서 DT[cond]의 형식으로 쓰인다.
(이때, cond는 논리형 벡터이다.)
dt[BMI>=30 & HGHT<150]
EXMD_BZ_YYYY <int> | RN_INDI <int> | HME_YYYYMM <int> | Q_PHX_DX_STK <int> | Q_PHX_DX_HTDZ <int> | Q_PHX_DX_HTN <int> | |
---|---|---|---|---|---|---|
2011 | 914987 | 201103 | 0 | 0 | 1 | |
2012 | 803990 | 201209 | NA | NA | NA | |
2014 | 149142 | 201412 | 0 | 0 | 0 |
미리 key들을 지정하면 더 빠르게 검색할 수 있다. 자세한 내용은 뒤에서 다루도록 하겠다.
column operation
열을 이름 또는 순번으로 선택하는 select는 DT[, .(cols)] 또는 DT[, list(cols)]의 형식으로 data.frame
과 비슷하나 몇 가지 차이점이 있다. 변수 이름으로 선택하는 경우 앞에 .()이나 list를 붙이지 않으면 결과로 벡터가 반환된다.
- 순번으로 열 선택
dt[, c(13, 14)]
HGHT <int> | WGHT <int> | |||
---|---|---|---|---|
144 | 61 | |||
162 | 51 | |||
163 | 65 | |||
152 | 51 | |||
159 | 50 | |||
157 | 55 | |||
160 | 56 | |||
159 | 54 | |||
156 | 53 | |||
146 | 48 |
- 이름으로 열 선택
## same
# dt[, list(HGHT, WGHT)]
dt[, .(HGHT, WGHT)]
HGHT <int> | WGHT <int> | |||
---|---|---|---|---|
144 | 61 | |||
162 | 51 | |||
163 | 65 | |||
152 | 51 | |||
159 | 50 | |||
157 | 55 | |||
160 | 56 | |||
159 | 54 | |||
156 | 53 | |||
146 | 48 |
열을 선택할 때 DT[, .(new_col = col)] 형식을 사용하여 열 이름을 지정해서 출력할 수 있다.
# rename
dt[, .(Height = HGHT, Weight = WGHT)]
Height <int> | Weight <int> | |||
---|---|---|---|---|
144 | 61 | |||
162 | 51 | |||
163 | 65 | |||
152 | 51 | |||
159 | 50 | |||
157 | 55 | |||
160 | 56 | |||
159 | 54 | |||
156 | 53 | |||
146 | 48 |
- 변수로 열 선택
## same
# vars <- c(13,14)
vars <- c("HGHT", "WGHT")
dt[, ..vars]
HGHT <int> | WGHT <int> | |||
---|---|---|---|---|
144 | 61 | |||
162 | 51 | |||
163 | 65 | |||
152 | 51 | |||
159 | 50 | |||
157 | 55 | |||
160 | 56 | |||
159 | 54 | |||
156 | 53 | |||
146 | 48 |
변수로 열을 선택하는 경우, 변수 앞에 ..을 붙이지 않으면 오류가 발생하므로 주의하도록 한다. .. 대신 with = F 를 뒤에 붙이거나 .SD, .SDcols 옵션을 사용하기도 한다. .SD는 뒤에서 더 자세히 다루도록 하겠다.
dt[, vars, with = F]
HGHT <int> | WGHT <int> | |||
---|---|---|---|---|
144 | 61 | |||
162 | 51 | |||
163 | 65 | |||
152 | 51 | |||
159 | 50 | |||
157 | 55 | |||
160 | 56 | |||
159 | 54 | |||
156 | 53 | |||
146 | 48 |
- 열 제외
필요없는 열을 제외할 때는 - 또는 ! 를 붙인다.
icols = c(1:12)
## same
# dt[, -..icols]
dt[, !..icols]
HGHT <int> | WGHT <int> | WSTC <int> | BMI <dbl> | VA_LT <dbl> | VA_RT <dbl> | BP_SYS <int> | BP_DIA <int> | URN_PROT <int> | HGB <dbl> | |
---|---|---|---|---|---|---|---|---|---|---|
144 | 61 | 90 | 29.4 | 0.7 | 0.8 | 120 | 80 | 1 | 12.6 | |
162 | 51 | 63 | 19.4 | 0.8 | 1.0 | 120 | 80 | 1 | 13.8 | |
163 | 65 | 82 | 24.5 | 0.7 | 0.6 | 130 | 80 | 1 | 15.0 | |
152 | 51 | 70 | 22.1 | 0.8 | 0.9 | 101 | 62 | 1 | 13.1 | |
159 | 50 | 73 | 19.8 | 0.7 | 0.8 | 132 | 78 | 1 | 13.0 | |
157 | 55 | 73 | 22.3 | 1.5 | 1.5 | 110 | 70 | 1 | 11.9 | |
160 | 56 | 67 | 21.9 | 1.5 | 1.5 | 119 | 78 | 1 | 11.2 | |
159 | 54 | 66 | 21.4 | 1.2 | 1.5 | 111 | 60 | 1 | 12.2 | |
156 | 53 | 67 | 21.8 | 1.2 | 1.0 | 138 | 72 | 1 | 11.0 | |
146 | 48 | 78 | 22.5 | 1.5 | 1.5 | 138 | 84 | 1 | 12.8 |
data.table
의 column operation에서는 열을 선택할뿐만 아니라 연산하는 식을 처리할 수 있다. 앞에서 배운 내용을 통해 특정 조건을 만족하는 행을 대상으로 mean
연산을 수행하여 보자.
by operation
by 옵션을 이용하여 그룹별로 함수를 적용할 수 있다. by=.(그룹1, 그룹2, …)을 사용해 두 개 이상의 그룹별로 함수를 적용할 수 있다. 이때 괄호 앞에 있는 점(‘.’)은 list()를 의미하므로 꼭 포함시키도록 한다. by 대신 keyby를 사용할 경우, 그룹별 집계 결과가 정렬되어 나타난다.
연도 변수인 EXMD_BZ_YYYY을 기준으로 집단을 분리한 후 각 집단의 HGHT와 WGHT, BMI 평균을 구하는 방법은 다음과 같다.
EXMD_BZ_YYYY <int> | HGHT <dbl> | WGHT <dbl> | BMI <dbl> | |
---|---|---|---|---|
2009 | 164.0841 | 64.32710 | 23.76402 | |
2010 | 164.9280 | 65.13983 | 23.82712 | |
2011 | 164.1480 | 64.89686 | 23.96009 | |
2012 | 164.9188 | 65.82051 | 24.08932 | |
2013 | 164.9095 | 64.90535 | 23.75391 | |
2014 | 164.3189 | 64.47244 | 23.78031 | |
2015 | 164.4792 | 66.07500 | 24.28167 |
기준으로 사용되지 않은 모든 열에 대해 평균을 구할때는 .SD를 사용한다. 이는 뒤에서 더 자세히 다루도록 하겠다.
dt[, lapply(.SD, mean), by=EXMD_BZ_YYYY]
EXMD_BZ_YYYY <int> | RN_INDI <dbl> | HME_YYYYMM <dbl> | Q_PHX_DX_STK <dbl> | Q_PHX_DX_HTDZ <dbl> | Q_PHX_DX_HTN <dbl> | |
---|---|---|---|---|---|---|
2009 | 499606.8 | 200908.2 | NA | NA | NA | |
2010 | 472277.1 | 201007.6 | NA | NA | NA | |
2011 | 510926.9 | 201107.8 | NA | NA | NA | |
2012 | 490263.9 | 201207.8 | NA | NA | NA | |
2013 | 483700.7 | 201308.1 | NA | NA | NA | |
2014 | 489703.6 | 201407.9 | NA | NA | NA | |
2015 | 491209.6 | 201508.7 | NA | NA | NA |
이번에는 두 개의 그룹 변수를 지정해 행의 개수를 출력해보자.
dt[HGHT >= 175, .N, by=.(EXMD_BZ_YYYY, Q_SMK_YN)]
EXMD_BZ_YYYY <int> | Q_SMK_YN <int> | N <int> | ||
---|---|---|---|---|
2009 | 1 | 7 | ||
2009 | NA | 1 | ||
2009 | 3 | 14 | ||
2009 | 2 | 5 | ||
2010 | 1 | 6 | ||
2010 | 3 | 19 | ||
2010 | 2 | 7 | ||
2011 | 1 | 7 | ||
2011 | 2 | 8 | ||
2011 | 3 | 16 |
정렬
그룹별로 함수를 적용한 결과를 정렬하고자 할 때는 keyby를 사용하거나 마지막에 [order()]를 붙인다.
- keyby
dt[HGHT >= 175, .N, keyby=.(EXMD_BZ_YYYY, Q_SMK_YN)]
EXMD_BZ_YYYY <int> | Q_SMK_YN <int> | N <int> | ||
---|---|---|---|---|
2009 | NA | 1 | ||
2009 | 1 | 7 | ||
2009 | 2 | 5 | ||
2009 | 3 | 14 | ||
2010 | 1 | 6 | ||
2010 | 2 | 7 | ||
2010 | 3 | 19 | ||
2011 | 1 | 7 | ||
2011 | 2 | 8 | ||
2011 | 3 | 16 |
by를 사용한 예제와는 달리 Q_SMK_YN에 대해서 정렬된 것을 볼 수 있다.
- [order()]
EXMD_BZ_YYYY <int> | HGHT <dbl> | WGHT <dbl> | BMI <dbl> | |
---|---|---|---|---|
2013 | 164.9095 | 64.90535 | 23.75391 | |
2009 | 164.0841 | 64.32710 | 23.76402 | |
2014 | 164.3189 | 64.47244 | 23.78031 | |
2010 | 164.9280 | 65.13983 | 23.82712 | |
2011 | 164.1480 | 64.89686 | 23.96009 | |
2012 | 164.9188 | 65.82051 | 24.08932 | |
2015 | 164.4792 | 66.07500 | 24.28167 |
EXMD_BZ_YYYY <int> | HGHT <dbl> | WGHT <dbl> | BMI <dbl> | |
---|---|---|---|---|
2015 | 164.4792 | 66.07500 | 24.28167 | |
2012 | 164.9188 | 65.82051 | 24.08932 | |
2011 | 164.1480 | 64.89686 | 23.96009 | |
2010 | 164.9280 | 65.13983 | 23.82712 | |
2014 | 164.3189 | 64.47244 | 23.78031 | |
2009 | 164.0841 | 64.32710 | 23.76402 | |
2013 | 164.9095 | 64.90535 | 23.75391 |
Expressions in by
by 옵션에는 변수뿐만 아니라 식을 지정할 수도 있다.
약물 치료 여부에 따른 환자수를 확인하려는 경우, 다음과 같이 식을 지정한다.
dt[, .N, by=.(Q_PHX_DX_STK > 0, Q_PHX_DX_HTDZ > 0)]
Q_PHX_DX_STK <lgl> | Q_PHX_DX_HTDZ <lgl> | N <int> | ||
---|---|---|---|---|
FALSE | FALSE | 1044 | ||
NA | NA | 565 | ||
FALSE | TRUE | 15 | ||
TRUE | TRUE | 3 | ||
TRUE | FALSE | 8 | ||
NA | TRUE | 8 | ||
TRUE | NA | 1 |
3. functions
- key를 이용한 탐색 setkey()
- 데이터 테이블 병합 merge()
- 데이터 테이블 수정 연산자 :=
- 특수 기호 .SD , .SDcols, .N
key를 이용한 탐색 setkey()
key를 사용하면 데이터의 탐색 및 처리 속도가 매우 향상된다. setkey(DT, col)로 키를 설정하며 키가 문자열 벡터일 경우 setkeyv을 활용한다. 설정되어 있는 키를 제거할 때는 setkey(DT, NULL)를 설정한다.
- 키 설정
setkey
를 활용해 데이터 테이블에 키를 설정하고, key
함수로 설정된 키를 확인할 수 있다.
- 키를 활용한 행 선택
dt[.(a)], dt[J(a)], dt[list(a)], dt[col == a] 중에서 아무거나 사용하여 행을 선택할 수 있다.
## same
# dt[.(2011)]
# dt[list(2011)]
# dt[EXMD_BZ_YYYY==2011]
dt[J(2011)]
EXMD_BZ_YYYY <int> | RN_INDI <int> | HME_YYYYMM <int> | Q_PHX_DX_STK <int> | Q_PHX_DX_HTDZ <int> | Q_PHX_DX_HTN <int> | |
---|---|---|---|---|---|---|
2011 | 979090 | 201112 | 0 | 0 | 0 | |
2011 | 99917 | 201107 | 0 | 0 | 0 | |
2011 | 763384 | 201111 | 0 | 0 | 0 | |
2011 | 199298 | 201107 | NA | NA | NA | |
2011 | 459837 | 201109 | 0 | 0 | 1 | |
2011 | 237719 | 201108 | 0 | 0 | 0 | |
2011 | 540586 | 201107 | 0 | 0 | 1 | |
2011 | 418137 | 201111 | 0 | 0 | 0 | |
2011 | 324678 | 201106 | 0 | 0 | 0 | |
2011 | 122776 | 201108 | NA | NA | NA |
## same
# dt[.(2011, 2)]
# dt[list(2011, 2)]
# dt[EXMD_BZ_YYYY==2011 & Q_HBV_AG==2]
dt[J(2011, 2)]
EXMD_BZ_YYYY <int> | RN_INDI <int> | HME_YYYYMM <int> | Q_PHX_DX_STK <int> | Q_PHX_DX_HTDZ <int> | Q_PHX_DX_HTN <int> | |
---|---|---|---|---|---|---|
2011 | 562083 | 201111 | 0 | 0 | 1 | |
2011 | 403607 | 201107 | NA | NA | NA | |
2011 | 917270 | 201107 | 0 | 0 | 1 | |
2011 | 831349 | 201108 | NA | NA | NA | |
2011 | 900114 | 201112 | 0 | 0 | 0 | |
2011 | 219397 | 201111 | 0 | 0 | 1 | |
2011 | 554112 | 201110 | NA | NA | NA | |
2011 | 793017 | 201106 | NA | NA | NA | |
2011 | 30622 | 201103 | NA | NA | NA | |
2011 | 326432 | 201111 | 0 | 0 | 0 |
데이터 테이블 병합 merge()
data.table
은 key를 사용하거나, on=을 활용하여 두 데이터 데이블을 병합할 수 있다.
기존 데이터를 가공하여 새로운 data.table인 dt1, dt2에 저장하고 연도에 따라 merge해 보자.
EXMD_BZ_YYYY <int> | RN_INDI <int> | HME_YYYYMM <int> | Q_PHX_DX_STK <int> | Q_PHX_DX_HTDZ <int> | Q_PHX_DX_HTN <int> | |
---|---|---|---|---|---|---|
2009 | 825387 | 200904 | 0 | 0 | 0 | |
2010 | 752067 | 201007 | 0 | 0 | 0 | |
2011 | 694083 | 201103 | NA | NA | NA | |
2012 | 699142 | 201210 | NA | NA | NA | |
2013 | 203606 | 201307 | 0 | 0 | 0 |
EXMD_BZ_YYYY <int> | RN_INDI <int> | HME_YYYYMM <int> | Q_PHX_DX_STK <int> | Q_PHX_DX_HTDZ <int> | Q_PHX_DX_HTN <int> | |
---|---|---|---|---|---|---|
2010 | 548365 | 201010 | NA | NA | NA | |
2011 | 432825 | 201112 | NA | NA | NA | |
2012 | 637115 | 201210 | 0 | 0 | 0 | |
2014 | 437139 | 201412 | 0 | 1 | 1 | |
2015 | 390119 | 201506 | 0 | 0 | 0 |
1. inner join
두 데이터에 모두 존재하는 경우 dt1[dt2, on=‘key’, nomatch=0] 또는 merge(dt1, dt2, by=‘key’, all=F) 형식 사용
dt1[dt2, on='EXMD_BZ_YYYY', nomatch=0]
# same
merge(dt1, dt2, by='EXMD_BZ_YYYY', all = F)
EXMD_BZ_YYYY <int> | RN_INDI <int> | HME_YYYYMM <int> | Q_PHX_DX_STK <int> | Q_PHX_DX_HTDZ <int> | Q_PHX_DX_HTN <int> | |
---|---|---|---|---|---|---|
2010 | 752067 | 201007 | 0 | 0 | 0 | |
2011 | 694083 | 201103 | NA | NA | NA | |
2012 | 699142 | 201210 | NA | NA | NA |
2. left_outer_join
첫 번째 데이터에 존재하는 경우 dt2[dt1, on=‘key’] 또는 merge(dt1, dt2, by=‘key’, all.x=T) 형식 사용
dt2[dt1, on='EXMD_BZ_YYYY']
# same
merge(dt1, dt2, by='EXMD_BZ_YYYY', all.x=T)
EXMD_BZ_YYYY <int> | RN_INDI <int> | HME_YYYYMM <int> | Q_PHX_DX_STK <int> | Q_PHX_DX_HTDZ <int> | Q_PHX_DX_HTN <int> | |
---|---|---|---|---|---|---|
2009 | NA | NA | NA | NA | NA | |
2010 | 548365 | 201010 | NA | NA | NA | |
2011 | 432825 | 201112 | NA | NA | NA | |
2012 | 637115 | 201210 | 0 | 0 | 0 | |
2013 | NA | NA | NA | NA | NA |
3. right_outer_join
두 번째 데이터에 존재하는 경우 dt1[dt2, on=‘key’] 또는 merge(dt1, dt2, by=‘key’, all.y=T)형식 사용
dt1[dt2, on='EXMD_BZ_YYYY']
# same
merge(dt1, dt2, by='EXMD_BZ_YYYY', all.y=T)
EXMD_BZ_YYYY <int> | RN_INDI <int> | HME_YYYYMM <int> | Q_PHX_DX_STK <int> | Q_PHX_DX_HTDZ <int> | Q_PHX_DX_HTN <int> | |
---|---|---|---|---|---|---|
2010 | 752067 | 201007 | 0 | 0 | 0 | |
2011 | 694083 | 201103 | NA | NA | NA | |
2012 | 699142 | 201210 | NA | NA | NA | |
2014 | NA | NA | NA | NA | NA | |
2015 | NA | NA | NA | NA | NA |
4. full_outer_join
어느 한 쪽에 존재하는 경우 merge(dt1, dt2, by=‘key’, all=T) 형식 사용
merge(dt1, dt2, by='EXMD_BZ_YYYY', all=TRUE)
EXMD_BZ_YYYY <int> | RN_INDI.x <int> | HME_YYYYMM.x <int> | Q_PHX_DX_STK.x <int> | Q_PHX_DX_HTDZ.x <int> | Q_PHX_DX_HTN.x <int> | |
---|---|---|---|---|---|---|
2009 | 825387 | 200904 | 0 | 0 | 0 | |
2010 | 752067 | 201007 | 0 | 0 | 0 | |
2011 | 694083 | 201103 | NA | NA | NA | |
2012 | 699142 | 201210 | NA | NA | NA | |
2013 | 203606 | 201307 | 0 | 0 | 0 | |
2014 | NA | NA | NA | NA | NA | |
2015 | NA | NA | NA | NA | NA |
데이터 테이블 수정 연산자 :=
데이터 테이블에서 열 j를 추가하거나 갱신 또는 삭제할 때 특수 기호 := 연산자를 사용한다. 수정 또는 생성할 열이 하나인 경우, dt[ , newcol1 := ] 형식을 쓰며 열이 두 개 이상인 경우 dt[, ‘:=’ (col1=, col2=)]을 사용한다.
다음의 예시를 통해서 자세히 알아보자.
기존의 데이터 테이블에서 HDL - LDL을 구해서 diff라는 이름의 열을 새로 생성한다. 그리고, HGHT와 WGHT는 HGHT*0.9, WGHT+5로 수정한다.
열 생성/수정
위 코드를 실행시키면 갱신이 눈에 보이지 않는 상태로 실행되며, 만약 갱신 결과를 눈에 보이도록 출력하려면 제일 뒤에 [] 를 붙여주어야 한다.
# 열 생성
dt[, diff := HDL-LDL][]
EXMD_BZ_YYYY <int> | RN_INDI <int> | HME_YYYYMM <int> | Q_PHX_DX_STK <int> | Q_PHX_DX_HTDZ <int> | Q_PHX_DX_HTN <int> | |
---|---|---|---|---|---|---|
2009 | 825387 | 200904 | 0 | 0 | 0 | |
2009 | 418137 | 200901 | 0 | 0 | 0 | |
2009 | 948405 | 200906 | 0 | 0 | 0 | |
2009 | 701404 | 200909 | NA | NA | NA | |
2009 | 79250 | 200901 | 0 | 0 | 0 | |
2009 | 714509 | 200907 | NA | NA | NA | |
2009 | 334101 | 200910 | 0 | 0 | 0 | |
2009 | 311226 | 200906 | 0 | 0 | 0 | |
2009 | 430621 | 200907 | 0 | 0 | 0 | |
2009 | 921924 | 200911 | 0 | 0 | 0 |
# 열 수정
dt[, ':=' (HGHT = HGHT*0.9, WGHT = WGHT+5)][]
EXMD_BZ_YYYY <int> | RN_INDI <int> | HME_YYYYMM <int> | Q_PHX_DX_STK <int> | Q_PHX_DX_HTDZ <int> | Q_PHX_DX_HTN <int> | |
---|---|---|---|---|---|---|
2009 | 825387 | 200904 | 0 | 0 | 0 | |
2009 | 418137 | 200901 | 0 | 0 | 0 | |
2009 | 948405 | 200906 | 0 | 0 | 0 | |
2009 | 701404 | 200909 | NA | NA | NA | |
2009 | 79250 | 200901 | 0 | 0 | 0 | |
2009 | 714509 | 200907 | NA | NA | NA | |
2009 | 334101 | 200910 | 0 | 0 | 0 | |
2009 | 311226 | 200906 | 0 | 0 | 0 | |
2009 | 430621 | 200907 | 0 | 0 | 0 | |
2009 | 921924 | 200911 | 0 | 0 | 0 |
열 삭제
데이터 테이블에서 열을 삭제하려면 col := NULL 형식을 사용한다.
# BMI 삭제
dt[, BMI := NULL]
특수 기호
.SD
.SD 는 ’Subset of Data’의 약자로, by로 지정한 그룹 칼럼을 제외한 모든 칼럼을 대상으로 연산을 수행할 때 사용한다.
# 모든 칼럼의 연도별 평균값
dt[, lapply(.SD, mean), by=EXMD_BZ_YYYY]
EXMD_BZ_YYYY <int> | RN_INDI <dbl> | HME_YYYYMM <dbl> | Q_PHX_DX_STK <dbl> | Q_PHX_DX_HTDZ <dbl> | Q_PHX_DX_HTN <dbl> | |
---|---|---|---|---|---|---|
2009 | 499606.8 | 200908.2 | NA | NA | NA | |
2010 | 472277.1 | 201007.6 | NA | NA | NA | |
2011 | 510926.9 | 201107.8 | NA | NA | NA | |
2012 | 490263.9 | 201207.8 | NA | NA | NA | |
2013 | 483700.7 | 201308.1 | NA | NA | NA | |
2014 | 489703.6 | 201407.9 | NA | NA | NA | |
2015 | 491209.6 | 201508.7 | NA | NA | NA |
또한 .SD 기호를 사용하여 연도별로 처음 두 개의 행을 추출할 수 있다.
dt[, head(.SD, 2), by=EXMD_BZ_YYYY]
EXMD_BZ_YYYY <int> | RN_INDI <int> | HME_YYYYMM <int> | Q_PHX_DX_STK <int> | Q_PHX_DX_HTDZ <int> | Q_PHX_DX_HTN <int> | |
---|---|---|---|---|---|---|
2009 | 825387 | 200904 | 0 | 0 | 0 | |
2009 | 418137 | 200901 | 0 | 0 | 0 | |
2010 | 792082 | 201006 | NA | NA | NA | |
2010 | 692726 | 201005 | 0 | 0 | 0 | |
2011 | 979090 | 201112 | 0 | 0 | 0 | |
2011 | 99917 | 201107 | 0 | 0 | 0 | |
2012 | 491031 | 201206 | 0 | 0 | 0 | |
2012 | 792082 | 201203 | NA | NA | 1 | |
2013 | 266165 | 201303 | NA | NA | 1 | |
2013 | 199298 | 201312 | NA | NA | NA |
.SDcols
.SDcols는 연산 대상이 되는 특정 칼럼을 지정하는 특수 기호이다. 특정 열을 대상으로 연산을 할 때 by 다음에 .SDcols = c(“col1”, “col2”, …)로 연산 대상을 지정한다.
EXMD_BZ_YYYY <int> | HGHT <dbl> | WGHT <dbl> | ||
---|---|---|---|---|
2009 | 132.9081 | 74.32710 | ||
2010 | 133.5917 | 75.13983 | ||
2011 | 132.9599 | 74.89686 | ||
2012 | 133.5842 | 75.82051 | ||
2013 | 133.5767 | 74.90535 | ||
2014 | 133.0983 | 74.47244 | ||
2015 | 133.2281 | 76.07500 |
.N
.N는 부분 데이터의 행의 수를 나타내며, 요약 통계치를 구할 때 대상 데이터의 수를 간편하게 구할 수 있다.
# 특정 조건을 만족하는 행의 수
dt[LDL >= 150, .N]
[1] 228
EXMD_BZ_YYYY <int> | N <int> | HGHT <dbl> | WGHT <dbl> | |
---|---|---|---|---|
2009 | 214 | 132.9081 | 74.32710 | |
2010 | 236 | 133.5917 | 75.13983 | |
2011 | 223 | 132.9599 | 74.89686 | |
2012 | 234 | 133.5842 | 75.82051 | |
2013 | 243 | 133.5767 | 74.90535 | |
2014 | 254 | 133.0983 | 74.47244 | |
2015 | 240 | 133.2281 | 76.07500 |
4. 데이터 재구조화 melt & dcast
마지막으로 효율적으로 data의 형태를 바꾸는 melt
(wide to long)와 dcast
(long to wide) 를 알아보겠다. melt
함수는 데이터 테이블을 녹여서 넓은 자료구조를 길게 재구조화하며, dcast
함수는 데이터 테이블을 주조하여 긴 자료구조를 넓게 재구조화한다.
melt
melt
함수는 일부 고정 칼럼을 제외한 나머지 칼럼을 stack 처리할 수 있다. melt(data, id.vars, measure.vars, variable.name, value.name) 형식으로 쓰이며, id.vars에는 고정 칼럼을 measure.vars는 stack 처리할 칼럼을 넣는다. melt
함수를 써서 길게 재구조화한 후의 “variable”, “value” 변수 이름을 바꾸고 싶다면 variable.name=“new_var_name”, value.name=“new_val_name” 처럼 새로운 칼럼 이름을 부여하여 지정할 수 있다.
EXMD_BZ_YYYY <int> | RN_INDI <int> | HGHT <dbl> | WGHT <dbl> | measure <fct> | val <int> |
---|---|---|---|---|---|
2009 | 825387 | 134.46 | 80 | TOT_CHOL | 193 |
2009 | 418137 | 142.56 | 74 | TOT_CHOL | 137 |
2009 | 948405 | 133.65 | 60 | TOT_CHOL | 159 |
2009 | 701404 | 126.36 | 71 | TOT_CHOL | 237 |
2009 | 79250 | 114.21 | 50 | TOT_CHOL | 160 |
2009 | 714509 | 137.70 | 79 | TOT_CHOL | 126 |
2009 | 334101 | 144.99 | 102 | TOT_CHOL | 175 |
2009 | 311226 | 141.75 | 102 | TOT_CHOL | 186 |
2009 | 430621 | 125.55 | 65 | TOT_CHOL | 203 |
2009 | 921924 | 147.42 | 120 | TOT_CHOL | 243 |
Enhanced melt
melt
함수는 동시에 여러 개의 칼럼들을 묶어서 사용할 수도 있다.
- list에 복수의 칼럼 이름을 입력하는 방법
melt
함수에 measure=list(col1, col2, …) 형식으로 여러 개의 칼럼 이름을 list() 형태로 넣는다. 이때 공통의 value.name을 지정할 수 있다.
EXMD_BZ_YYYY <int> | RN_INDI <int> | HME_YYYYMM <int> | Q_PHX_DX_STK <int> | Q_PHX_DX_HTDZ <int> | Q_PHX_DX_HTN <int> | |
---|---|---|---|---|---|---|
2009 | 825387 | 200904 | 0 | 0 | 0 | |
2009 | 418137 | 200901 | 0 | 0 | 0 | |
2009 | 948405 | 200906 | 0 | 0 | 0 | |
2009 | 701404 | 200909 | NA | NA | NA | |
2009 | 79250 | 200901 | 0 | 0 | 0 | |
2009 | 714509 | 200907 | NA | NA | NA | |
2009 | 334101 | 200910 | 0 | 0 | 0 | |
2009 | 311226 | 200906 | 0 | 0 | 0 | |
2009 | 430621 | 200907 | 0 | 0 | 0 | |
2009 | 921924 | 200911 | 0 | 0 | 0 |
- 특정 패턴을 정규 표현식으로 매칭하는 방법
melt
함수에 measure=patterns() 형식으로 특정 패턴을 따르는 복수의 칼럼을 정규 표현식을 통해 설정한다.
EXMD_BZ_YYYY <int> | RN_INDI <int> | HME_YYYYMM <int> | Q_HBV_AG <int> | Q_SMK_YN <int> | Q_DRK_FRQ_V09N <int> | HGHT <dbl> | WGHT <dbl> | WSTC <int> | |
---|---|---|---|---|---|---|---|---|---|
2009 | 825387 | 200904 | NA | 1 | 1 | 134.46 | 80 | 85 | |
2009 | 418137 | 200901 | NA | 2 | NA | 142.56 | 74 | 80 | |
2009 | 948405 | 200906 | 1 | 1 | 0 | 133.65 | 60 | 70 | |
2009 | 701404 | 200909 | 1 | 1 | 0 | 126.36 | 71 | 77 | |
2009 | 79250 | 200901 | 1 | 1 | 0 | 114.21 | 50 | 69 | |
2009 | 714509 | 200907 | 1 | 1 | 0 | 137.70 | 79 | 81 | |
2009 | 334101 | 200910 | 1 | 1 | 2 | 144.99 | 102 | 88 | |
2009 | 311226 | 200906 | 1 | NA | 0 | 141.75 | 102 | 98 | |
2009 | 430621 | 200907 | 1 | 1 | 0 | 125.55 | 65 | 77 | |
2009 | 921924 | 200911 | 1 | 3 | 2 | 147.42 | 120 | 105 |
dcast
dcast
함수는 melt
함수를 통해 길게 쌓여진 칼럼을 각 항목별로 분리하기 위해 사용한다. dcast(data, formula, value.var, fun.aggregate) 형식으로 쓰이며, formula의 LHS에는 id.vars, RHS에는 variable을 입력하고 value.name에는 “value”를 입력한다. 이때 “variable”과 “value” 변수 이름을 바꿨다면, 새로 지정한 칼럼명을 넣는다.
# long to wide
dt.wide1 <- dcast(dt.long1, EXMD_BZ_YYYY + RN_INDI + HGHT + WGHT ~ measure, value.var = "val")
dt.wide1
EXMD_BZ_YYYY <int> | RN_INDI <int> | HGHT <dbl> | WGHT <dbl> | TOT_CHOL <int> | HDL <int> | LDL <int> |
---|---|---|---|---|---|---|
2009 | 4263 | 141.75 | 76 | 264 | 81 | 172 |
2009 | 4664 | 129.60 | 63 | 192 | 57 | 120 |
2009 | 9866 | 127.98 | 86 | 112 | 48 | 40 |
2009 | 17079 | 136.89 | 64 | 161 | 72 | 78 |
2009 | 26776 | 139.32 | 88 | 189 | 59 | 117 |
2009 | 27226 | 120.69 | 66 | 206 | 101 | 96 |
2009 | 38967 | 140.94 | 90 | 241 | 47 | 164 |
2009 | 42231 | 140.13 | 75 | 123 | 39 | 66 |
2009 | 46621 | 140.13 | 92 | 145 | 36 | 59 |
2009 | 53748 | 132.84 | 60 | 165 | 73 | 69 |
dcast
함수에 집계 함수(fun.aggregate)를 사용하여 그룹별 요약 통계량을 계산한 결과를 재구조화하여 반환할 수 있다.
# 연도별 TOT_CHOL, HDL, LDL의 평균값
dt.wide2 <- dcast(dt.long1, EXMD_BZ_YYYY ~ measure, value.var = "val", fun.aggregate = mean, na.rm =T)
dt.wide2
EXMD_BZ_YYYY <int> | TOT_CHOL <dbl> | HDL <dbl> | LDL <dbl> | |
---|---|---|---|---|
2009 | 195.0748 | 55.61215 | 150.9486 | |
2010 | 194.7669 | 55.15678 | 112.9914 | |
2011 | 194.1076 | 57.31390 | 112.9450 | |
2012 | 198.6154 | 55.94872 | 117.5259 | |
2013 | 192.1193 | 55.46091 | 111.1577 | |
2014 | 194.9646 | 55.64173 | 116.5455 | |
2015 | 194.9542 | 56.27083 | 111.5294 |
Enhanced dcast
dcast
함수의 value.vars에 복수의 칼럼을 넣어 여러 개의 칼럼을 동시에 재구조화할 수 있다.
EXMD_BZ_YYYY <int> | RN_INDI <int> | HME_YYYYMM <int> | Q_PHX_DX_STK <int> | Q_PHX_DX_HTDZ <int> | Q_PHX_DX_HTN <int> | |
---|---|---|---|---|---|---|
2009 | 4263 | 200910 | NA | NA | NA | |
2009 | 4664 | 200910 | 0 | 0 | 0 | |
2009 | 9866 | 200912 | NA | NA | NA | |
2009 | 17079 | 200911 | 0 | 0 | 0 | |
2009 | 26776 | 200902 | NA | NA | NA | |
2009 | 27226 | 200908 | 0 | 0 | 0 | |
2009 | 38967 | 200910 | 0 | 0 | 0 | |
2009 | 42231 | 200911 | 0 | 0 | 0 | |
2009 | 46621 | 200912 | NA | NA | NA | |
2009 | 53748 | 200906 | 0 | 0 | 0 |
마치며
-
data.table
패키지를 사용하여 쉽고 빠르게 데이터를 가공할 수 있다. -
data.table
의 기본 문법은 DT[i, j, by] 형태이며 그 쓰임새는 다음과 같다.- i : 행을 선택
- j : 열을 선택, 일정한 함수 적용
- by : 집단을 구성, j에서 지정한 열과 함수를 by 그룹 별로 수행
- key를 사용하여 데이터를 빠르게 탐색 및 처리할 수 있다.
- 열을 추가하거나 갱신 또는 삭제할 때 := 연산자를 사용한다.
-
data.table
에는 여러 특수 기호가 존재한다.- .SD : 그룹 칼럼을 제외한 모든 칼럼을 대상으로 연산
- .SDcols : 연산 대상이 되는 특정 칼럼을 지정
- .N : 부분 데이터의 행의 개수
-
melt
와dcast
함수를 사용하여 데이터를 재구조화할 수 있다.
cheatsheet
Citation
@online{lee2022,
author = {Lee, Yujin},
title = {Data.table {패키지} {소개}},
date = {2022-02-11},
url = {https://blog.zarathu.com/posts/2022-02-11-datatable/},
langid = {en}
}