続・複数のファイルを一度に読みこむ方法
前々回の続編。
前々回の記事を書いた後の反応にこんなのがありました。
foreach (i=list.files)%dopar%fread(i) 的な処理はよくやる https://t.co/x4H4sLqh2E
— kato.kohaku.0 (@kato_kohaku) July 7, 2019
なるほど、並列化という手があったか。
ということで、まだ動作が不安定な 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というところは変わらない模様。
# 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")