続・複数のファイルを一度に読みこむ方法

前々回の続編。

前々回の記事を書いた後の反応にこんなのがありました。

なるほど、並列化という手があったか。

ということで、まだ動作が不安定な vroom を外して、

  • Tidyverse流(read_csv & map_dfr)
  • data.table流(fread & rbindlist)
  • data.table流並列化(fread & rbindlist & foreach)

の3パターンで試します。

デモデータの用意

必要なパッケージを読み込みつつ、実験用のデモデータの用意。

library(tidyverse)
library(data.table)
library(doParallel)
library(microbenchmark)

DIR <- "data/"

row_n <- 10000
col_n <- 10
files <- 365 # 前回は不安定なvroomに合わせて10ファイルだったのを増やした。
file_names <- paste0(DIR, formatC(1:files, width = 3, flag = "0"), ".csv")

for (i in file_names) {
  rnorm(col_n * row_n) %>%
    round(4) %>%
    matrix(row_n, col_n) %>%
    data.frame() %>%
    fwrite(i)
}

Tidyverse流

  DIR %>%
    list.files(pattern = "*.csv", full.names = TRUE) %>%
    set_names() %>%
    map_dfr(
      read_csv,
      progress = FALSE,
      .id = "file_name"
    ) -> dat

data.table流

  file_names <- list.files(DIR, pattern = "*.csv", full.names = TRUE)
  tmp <- map(file_names, fread, showProgress = FALSE)
  setattr(tmp, "names", file_names)
  dat <- rbindlist(tmp, idcol = "file_name")

data.table流並列化

cores <- detectCores(logical = FALSE) # コア数確認
cl <- makeCluster(cores)
registerDoParallel(cl)
clusterEvalQ(cl, c(library(data.table), library(foreach)))
file_names <- list.files(DIR, pattern = "*.csv", full.names = TRUE)
tmp <- foreach(i = file_names) %dopar% {
    fread(i, showProgress = FALSE)
  }
setattr(tmp, "names", file_names)
rbindlist(tmp, idcol = "files")

比較

ファイル数が増えると Tidyverse 流では遅く感じてしまう。
やはり、data.table流は速い。
期待した並列化だが、バラツキが大きくて一概に速いとは言えない状況。コア数多めのCPU積んでる場合はいいのかもしれない。
書きやすさのTidyverse、速さのdata.tableというところは変わらない模様。

f:id:bob3:20190709010624p:plain
複数ファイルの読み込み速度比較(並列化入り)

# demo data ---------------------------------------------------------------
library(tidyverse)
library(data.table)
library(doParallel)
library(microbenchmark)

DIR <- "data/"

row_n <- 10000
col_n <- 10
files <- 365
file_names <- paste0(DIR, formatC(1:files, width = 3, flag = "0"), ".csv")

for (i in file_names) {
  rnorm(col_n * row_n) %>%
    round(4) %>%
    matrix(row_n, col_n) %>%
    data.frame() %>%
    fwrite(i)
}


# read_csv + map_dfr -----------------------------------------------------------------
read_csv_and_map_dfr <- function() {
  DIR %>%
    list.files(pattern = "*.csv", full.names = TRUE) %>%
    set_names() %>%
    map_dfr(
      read_csv,
      progress = FALSE,
      .id = "file_name"
    )
}

# fread + rbindlist ---------------------------------------------------------
fread_and_rbindlist <- function() {
  file_names <- list.files(DIR, pattern = "*.csv", full.names = TRUE)
  tmp <- map(file_names, fread, showProgress = FALSE)
  setattr(tmp, "names", file_names)
  rbindlist(tmp, idcol = "file_name")
}

# 並列化 ---------------------------------------------------------------------
cores <- detectCores(logical = FALSE) # コア数確認
cl <- makeCluster(cores)
registerDoParallel(cl)
clusterEvalQ(cl, c(library(data.table), library(foreach)))

my_foreach <- function() {
  file_names <- list.files(DIR, pattern = "*.csv", full.names = TRUE)
  tmp <- foreach(i = file_names) %dopar% {
    fread(i, showProgress = FALSE)
  }
  setattr(tmp, "names", file_names)
  rbindlist(tmp, idcol = "files")
}
# stopCluster(cl)

# microbenchimark ---------------------------------------------------------

gc()
gc()

compare <- microbenchmark(
  "read_csv & map_dfr" = read_csv_and_map_dfr(),
  "fread & rbindlist" = fread_and_rbindlist(),
  "doParallel & fread & rbindlist" = my_foreach(),
  times = 10L
)

compare

autoplot(compare) + labs(title = "R 3.6.1")