R data.table(데이터 테이블)
R data.table
- R shiny Korea meetup에서 R 데이터 테이블을 소개하는 발표를 했습니다.
CRUD(Create/Read/Update/Delete)
CRUD 정도는 DT[i/filter, j/summarise, by]
정도에서 모두 끝낼 수 있습니다.
DT[i, j/summarise/func, by]
에서 j/summarise/func
부분을 봅시다. 이 부분에서 데이터테이블의 열(column)을 선택하거나, 열을 요약하는 함수를 적용하거나, 일반적인 함수를 적용할 수 있습니다.
- 열 :
DT[, c(2,3)]
- 요약 :
DT[, mean(hp), cyl]
- 일반적인 함수 :
DT[, plot(hp~mpg)]
실제로 해볼까요?
library(data.table)
library(dplyr)
data(mtcars)
DT = data.table(mtcars)
DT[,c(2,3)] %>% head
## cyl disp ## 1: 6 160 ## 2: 6 160 ## 3: 4 108 ## 4: 6 258 ## 5: 8 360 ## 6: 6 225
DT[, mean(hp), cyl]
## cyl V1 ## 1: 6 122.28571 ## 2: 4 82.63636 ## 3: 8 209.21429
DT[, plot(hp~mpg)]
## NULL
깔끔하죠?
열이름을 가진 변수
DT[,j/func]
에서 j
는 열을 선택하고, 열이름을 따옴표 없이 쓸 수 있다는 장점이 있습니다.
DT[, cyl] %>% head
## [1] 6 6 4 6 8 6
첫 번째 문제는 열이름이 변수에 들어 있을 때입니다.
cyl = c('hp', 'mpg')
DT[, cyl] %>% head
## [1] 6 6 4 6 8 6
변수 cyl
의 c('hp', 'mpg')
, 열이름 hp
와 mpg
를 선택하려면 어떻게 해야 할까요?
DT[, ..cyl] %>% head
## hp mpg ## 1: 110 21.0 ## 2: 110 21.0 ## 3: 93 22.8 ## 4: 110 21.4 ## 5: 175 18.7 ## 6: 105 18.1
보셨나요? ..
을 씁니다.
열 순번이 있을 때에도 마찬가지입니다.
icols = c(3,4,5)
DT[, ..icols] %>% head
## disp hp drat ## 1: 160 110 3.90 ## 2: 160 110 3.90 ## 3: 108 93 3.85 ## 4: 258 110 3.08 ## 5: 360 175 3.15 ## 6: 225 105 2.76
그냥 변수로 사용할 때
DT[,j/func,]
에서 func
는 어떤 함수도 가능합니다. 예를 들어 hp
가 100
보다 큰지 확인하고 싶다면,
DT[ , hp > 100]
## [1] TRUE TRUE FALSE TRUE TRUE TRUE TRUE FALSE FALSE TRUE TRUE TRUE TRUE TRUE ## [15] TRUE TRUE TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE FALSE FALSE TRUE ## [29] TRUE TRUE TRUE TRUE
홀수번째는 100
, 짝수번째는 150
보다 큰지 확인하고자 한다면,
DT[, hp>c(100, 150)]
## [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE FALSE FALSE FALSE TRUE TRUE TRUE TRUE ## [15] TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE FALSE FALSE FALSE ## [29] TRUE TRUE TRUE FALSE
만약 c(100,150)
이 변수에 담겨 있다면 어떻게 해야 할까요?
threshold = c(100,150)
DT[, hp>threshold]
## [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE FALSE FALSE FALSE TRUE TRUE TRUE TRUE ## [15] TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE FALSE FALSE FALSE ## [29] TRUE TRUE TRUE FALSE
그런데 이름이 공교롭게 hp
라면?
hp = c(100,150)
DT[, hp>hp] #???
## [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE ## [15] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE ## [29] FALSE FALSE FALSE FALSE
이런 말이 떠오릅니다. Namespace는 좋은 것이다!?
StackOverflow의 해법을 소개합니다.
.. <- function(..., .env=sys.parent(2)) {
get(deparse(substitute(...)), env = .env)
}
DT[, hp > ..(hp)]
## [1] TRUE FALSE FALSE FALSE TRUE FALSE TRUE FALSE FALSE FALSE TRUE TRUE TRUE TRUE ## [15] TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE FALSE FALSE FALSE ## [29] TRUE TRUE TRUE FALSE
위의 경우와 결과가 일치하는지 한 번 확인해 보시죠.
결론
결과적으로 hp
는 데이터 테이블의 열이름일 수도 있고, 열이름을 담고 있는 열이름 변수일 수도 있고, 아니면 데이터 테이블이 존재하는 환경(이를 부모 환경이라고 합시다)의 어떤 변수일 수도 있습니다.
DT[, x] # 열이름
DT[, ..x] # 열이름 변수
DT[, ..(x)] # 부모 환경의 변수
사실 DT[, ..x]
에서 x
도 부모 환경의 변수이긴 합니다만, 열이름 또는 열순번을 나타내는 것으로 해석된다는 점에서 부모 환경 변수와 다르겠습니다. 함수 ..()
는 다음과 같이 미리 정의해 두어야 한다는 점을 잊지 마세요.
.. <- function(..., .env=sys.parent(2)) {
get(deparse(substitute(...)), env = .env)
}
부록
다음의 두 예에서 4
는 어떻게 다른지 설명해보세요.
DT[, 4] %>% head
## hp ## 1: 110 ## 2: 110 ## 3: 93 ## 4: 110 ## 5: 175 ## 6: 105
DT[, cyl>4]
## [1] TRUE TRUE FALSE TRUE TRUE TRUE TRUE FALSE FALSE TRUE TRUE TRUE TRUE TRUE ## [15] TRUE TRUE TRUE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE FALSE FALSE FALSE ## [29] TRUE TRUE TRUE FALSE
그렇다면 다음의 결과는 어떻게 될까요?
DT[, (4)]
Leave a comment