[R] data.table

강콩콩·2023년 6월 26일
0

R

목록 보기
1/1
post-thumbnail

대용량 Data (> 1,000,000,000 rows)를 다루다 보면, 단순 dplyr로는 성능이 부족할 때가 있다.
data.table로 성능 향상을 기대해보자.

기본 활용

make groupby & summary table

  • .()에 필요한 집계 함수를 넣어주고, by에 집계할 group(s)를 넘겨주면 된다.
library(data.table)

# 예시 데이터 생성
dt <- data.table(
  group = rep(c("A", "B", "C"), each = 4),
  x = 1:12,
  y = 13:24
)

# 집계 연산 수행
result <- dt[, .(sum_x = sum(x), mean_y = mean(y)), by = group]
print(result)
  • custom function을 지원하며, row를 iteration 하는 로직을 넣지 않는 한 벡터 연산으로 빠른 처리 속도를 보인다.
library(data.table)

# 대량의 데이터 생성
N <- 1000000
dt <- data.table(
  group1 = sample(LETTERS[1:5], N, replace = TRUE),
  group2 = sample(LETTERS[6:10], N, replace = TRUE),
  V1 = rnorm(N),
  V2 = runif(N),
  V3 = rpois(N, lambda = 10)
)

# 벡터 연산을 사용하는 custom function 정의
custom_function <- function(x, y) {
  result <- x + y^2
  return(result)
}

# custom function을 data.table에 적용
dt[, custom_result := custom_function(V1, V2), by = .(group1, group2)]

add new rows

  • [, V4 := V1 + V2 + V3] 형식으로 추가해주면 된다.
# add new row

library(data.table)

# 기존 데이터
dt <- data.table(
  V1 = 1:3,
  V2 = 4:6,
  V3 = 7:9
)

# 1, 2, 3번 컬럼 값을 조합하여 4번 열 생성
dt[, V4 := V1 + V2 + V3]

print(dt)

pivot

  • melt 함수 활용
# Pivot (Wide to Long)

library(data.table)

# 기존 데이터
dt_wide <- data.table(
  id = c(1, 2, 3),
  A = c(10, 20, 30),
  B = c(40, 50, 60),
  C = c(70, 80, 90)
)

dt_long <- melt(dt_wide, id.vars = "id", variable.name = "variable", value.name = "value")
print(dt_long)

unpivot

  • dcast 함수 활용
# Unpivot (Long to Wide)

library(data.table)

# 기존 데이터
dt_long <- data.table(
  id = c(1, 1, 2, 2, 3, 3),
  variable = c("A", "B", "A", "B", "A", "B"),
  value = c(10, 40, 20, 50, 30, 60)
)

dt_wide <- dcast(dt_long, id ~ variable, value.var = "value")
print(dt_wide)

dplyr, plyr package benchmark

https://h2oai.github.io/db-benchmark/
https://rfriend.tistory.com/557

  • dummy 데이터로 benchmark 실습
library(data.table)
library(plyr)
library(dplyr)

# 대량의 데이터 생성
N <- 100000000
dt <- data.table(
  group1 = sample(LETTERS[1:5], N, replace = TRUE),
  group2 = sample(LETTERS[6:10], N, replace = TRUE),
  V1 = rnorm(N),
  V2 = runif(N),
  V3 = rpois(N, lambda = 10)
)
df <- as.data.frame(dt)

# 시간 측정 반복 횟수
repetitions <- 5

# data.table 연산 시간 측정
dt_times <- replicate(repetitions, {
  dt_copy <- copy(dt)
  system.time({
    dt_copy[, V4 := V1 + V2 + V3]
    dt_copy[, V5 := V1 * V2 + V3]
    dt_copy[, V6 := V4 + V5]
    dt_aggregated <- dt_copy[, .(sum_V4 = sum(V4), mean_V1 = mean(V1), max_V5 = max(V5)), by = .(group1, group2)]
  })["elapsed"]
})

# plyr 연산 시간 측정
plyr_times <- replicate(repetitions, {
  df_copy <- copy(df)
  system.time({
    df_copy <- transform(df_copy, V4 = V1 + V2 + V3)
    df_copy <- transform(df_copy, V5 = V1 * V2 + V3)
    df_copy <- transform(df_copy, V6 = V4 + V5)
    plyr_aggregated <- ddply(df_copy, .(group1, group2), summarize, sum_V4 = sum(V4), mean_V1 = mean(V1), max_V5 = max(V5), .groups = 'drop')
  })["elapsed"]
})

# dplyr 연산 시간 측정
dplyr_times <- replicate(repetitions, {
  df_copy <- copy(df)
  system.time({
    df_copy <- df_copy %>% mutate(V4 = V1 + V2 + V3)
    df_copy <- df_copy %>% mutate(V5 = V1 * V2 + V3)
    df_copy <- df_copy %>% mutate(V6 = V4 + V5)
    dplyr_aggregated <- df_copy %>% group_by(group1, group2) %>% summarize(sum_V4 = sum(V4), mean_V1 = mean(V1), max_V5 = max(V5), .groups = 'drop')
  })["elapsed"]
})

# 평균 소요 시간 계산
dt_avg_time <- mean(dt_times)
plyr_avg_time <- mean(plyr_times)
dplyr_avg_time <- mean(dplyr_times)

# 소수점 이하 자릿수를 2자리로 설정하여 출력
print(paste("data.table 평균 연산 소요 시간:", round(dt_avg_time, 2)))
print(paste("plyr 평균 연산 소요 시간:", round(plyr_avg_time, 2)))
print(paste("dplyr 평균 연산 소요 시간:", round(dplyr_avg_time, 2)))

[1] "data.table 평균 연산 소요 시간: 8.05"
[1] "plyr 평균 연산 소요 시간: 45.9"
[1] "dplyr 평균 연산 소요 시간: 10.97"

  • 생각보다 dynamic 하지 않은 case도 존재하는 듯 하다.
  • 그래도 전반적으로 압도적 성능을 보인다.
profile
MLOps, ML Engineer. 데이터에서 시스템으로, 시스템에서 가치로.

0개의 댓글