R に Intel MKL を導入する
Rを高速化する方法として、Rblas.dllを入れ替えるという手法は以前からあった。
ATLASとかGotoBLASとか。
BLASの高速化 - RjpWiki
今回、Microsoft R Openで使われている Intel MKL をRblas.dllとして利用する方法が紹介されていたので ここにメモをしておく。
教えてもらったツイートはこちら。
素のRにintel MKLを導入する方法を知ったのでMicrosoft R Openは卒業する。https://t.co/tkSQpSrPU7
— fqz (@fqz7c3) 2020年1月8日
ネタ元はこちらのTom Wenseleersさんのコメント。
MRO 3.6 coming?
さらにその元ネタはこちら。
Linking Intel's Math Kernel Library (MKL) to R on Windows - Stack Overflow
1. Intel MKL (Math Kernel Library) をインストールする。配布元はこちら。Customizable Package でOK。
https://software.intel.com/en-us/mkl/choose-download
2.MKLのインストール先の以下の中身を
C:\Program Files (x86)\IntelSWTools\compilers_and_libraries\windows\redist\intel64\mkl C:\Program Files (x86)\IntelSWTools\compilers_and_libraries\windows\redist\intel64\compiler
Rのインストール先の以下のフォルダにコピーする。
C:\Program Files\R\R-3.x.x\bin\x64
3. 元々入っている R の Rblas.dll と Rlapack.dll を適当にリネームする。
4. C:\Program Files\R\R-3.x.x\bin\x64 内に mkl_rt.dll のコピーを二つ作り、それぞれ Rblas.dll と Rlapack.dll にリネームする。
broomパッケージを使ってみる
broomパッケージの上手な使い方を考えてる。
library(stargazer) library(broom) library(dplyr) library(ggplot2) res1 <- lm(yield ~ block + N + P + K, npk) res2 <- lm(yield ~ block + N + P * K, npk) # res <- res1 res <- res2 anova(res) res %>% aov() %>% tidy() summary(res) tidy(res) # 多重比較 res %>% aov() %>% TukeyHSD() %>% tidy() %>% mutate(pair = paste(term, comparison, sep="_")) %>% ggplot(aes(colour=cut(adj.p.value, c(0, 0.01, 0.05, 1), label=c("p<0.01","p<0.05","Non-Sig")))) + geom_hline(yintercept=0, lty="11", colour="grey30") + geom_errorbar(aes(pair, ymin=conf.low, ymax=conf.high), width=0.2) + geom_point(aes(pair, estimate)) + labs(colour="significance", title = "Tukey Honest Significant Differences") + coord_flip() glance(res) augment(res) res %>% tidy(conf.int = TRUE) %>% filter(term != "(Intercept)") %>% ggplot(aes(estimate, term, color = term)) + geom_point() + geom_errorbarh(aes(xmin = conf.low, xmax = conf.high)) + geom_vline(xintercept = 0) stargazer(res1, res2, type="text", column.labels = c("res1","res2"),)
続々・複数のファイルを一度に読みこむ方法
過去2回の続きでこれが最後(多分)。
複数のファイルを一度に読みこむ方法 - bob3’s blog
続・複数のファイルを一度に読みこむ方法 - bob3’s blog
前回、data.table::fread() を foreach で並列化したパターンを追加したのだけど、data.table::fread() はそもそも並列化されているということを思い出しました。
大変な無駄をしていたわけです。
このままだと並列化によって速くなるか否かが不明なままですので、readr::read_csv() を並列化して速さを確認してみます。
結論から言うとやっぱり fread & rbindlist が最速。
でも read_csv() も並列化した分は速くなってます。
まあ、通常は fread & rbindlist でいいと思います。
# 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(readr), library(foreach))) my_foreach <- function() { file_names <- list.files(DIR, pattern = "*.csv", full.names = TRUE) tmp1 <- foreach(i = file_names) %dopar% { read_csv(i) } bind_rows(tmp1, .id = "files") %>% left_join(tibble(file_names = file_names, files = as.character(1:length(file_names))), by = "files") %>% select(-files) } # microbenchimark --------------------------------------------------------- gc() gc() compare <- microbenchmark( "read_csv & map_dfr" = read_csv_and_map_dfr(), "fread & rbindlist" = fread_and_rbindlist(), "doParallel & read_csv & bind_rows" = my_foreach(), times = 30L ) compare autoplot(compare) + labs(title = "R 3.6.1")
続・複数のファイルを一度に読みこむ方法
前々回の続編。
前々回の記事を書いた後の反応にこんなのがありました。
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")
base と Tidyverse と data.table と
「恋しさと せつなさと 心強さと」といえば『ストリートファイターII MOVIE』の挿入歌ですね。
さて、初心者にRを教えるとき、base準拠にすべきか、Tidyverse準拠にすべきか、という議論があります。
これは私も悩むところでありまして、さらに言えば data.table ってのもあります。
んで、まずはこの三つの流派(?)が存在してそれぞれこんな書き方をするんだよ、というのを示せるといいのかなーと思うわけです。
そこで試しにこんな処理を base, Tidyverse, data.tableのそれぞれで書いてみました。
こんな風に書いてみましたが、それぞれの書き方の違いの雰囲気ぐらいは伝わるかな?
base
library(nycflights13) flights.df <- na.omit(as.data.frame(flights)[, c("dep_delay", "arr_delay", "carrier", "air_time", "distance")]) flights.df$HourlySpeed <- (flights.df$distance * 1.60934) / (flights.df$air_time / 60) flights.df2 <- aggregate( flights.df[, c("dep_delay", "arr_delay", "HourlySpeed") ], list(carrier = flights.df$carrier), mean ) flights.df2$n <- table(flights.df$carrier) flights.df2[order(flights.df2$n, decreasing = TRUE), ]
Tidyverse
library(nycflights13) library(tidyverse) flights %>% select(dep_delay, arr_delay, carrier, air_time, distance) %>% drop_na() %>% mutate(HourlySpeed = (distance * 1.60934) / (air_time / 60)) %>% group_by(carrier) %>% summarise( mean_dep_delay = mean(dep_delay), mean_arr_delay = mean(arr_delay), mean_HourlySpeed = mean(HourlySpeed), n = n() ) %>% arrange(desc(n))
data.table
library(nycflights13) library(data.table) flights.dt <- na.omit(as.data.table(flights)[, c("dep_delay", "arr_delay", "carrier", "air_time", "distance")]) setorder(flights.dt[ , HourlySpeed := (distance * 1.60934) / (air_time / 60) ][ j = list( mean_dep_delay = mean(dep_delay), mean_arr_delay = mean(arr_delay), mean_HourlySpeed = mean(HourlySpeed), n = .N ), by = carrier ], -n) -> flights.dt2 flights.dt2
実行速度は?
data.table圧勝!……かと思いきや、これくらいのデータ量だとTidyverseと大差ないですね。
複数のファイルを一度に読みこむ方法
Rで複数のファイルを一度に読み込みたい場合がたまにある。
例えば、売り上げのデータが日付ごとに分かれたファイルに収められているような場合。
しょっちゅう発生するわけでもなく、いつも忘れてしまって調べるのに時間がかかるのでここにメモしておく。
ここではそうしたファイルが一つのフォルダに収められているものとする。
方法は大きく三つある
- Tidyverse流(read_csv & map_dfr)
- data.table流(fread & rbindlist)
- vroom流(vroom)
デモデータの用意
必要なパッケージを読み込みつつ、実験用のデモデータの用意。
library(tidyverse) library(data.table) library(vroom) library(microbenchmark) DIR <- "data/" row_n <- 10000 col_n <- 10 files <- 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, .id = "file_name" ) -> dat
data.table流
DIR %>% list.files(pattern = "*.csv", full.names = TRUE) %>% set_names() %>% map(fread) %>% rbindlist(idcol = "file_name") -> dat
vroom流
DIR %>% list.files(pattern = "*.csv", full.names = TRUE) %>% vroom(id = "file_name") -> dat
比較
Tidyverseに慣れてる人なら read_csv と map_dfr を組み合わせるのが分かりやすいだろう。
data.table流はとにかく速い。爆速。
vroomはファイル数が多くなるとエラーになる。今後に期待。
# read_csv + map_dfr ----------------------------------------------------------------- read_csv_and_map_dfr <- function() { DIR %>% list.files(pattern = "*.csv", full.names = TRUE) %>% set_names() %>% map_dfr(read_csv, .id = "file_name" ) } # fread + rbindlist --------------------------------------------------------- fread_and_rbindlist <- function() { DIR %>% list.files(pattern = "*.csv", full.names = TRUE) %>% set_names() %>% map(fread) %>% rbindlist(idcol = "file_name") } # vroom ------------------------------------------------------------------- # ファイル数が多いとエラーになる場合がある。 my_vroom <- function() { DIR %>% list.files(pattern = "*.csv", full.names = TRUE) %>% vroom(id = "file_name") } # microbenchimark --------------------------------------------------------- gc() gc() compare <- microbenchmark( "read_csv & map_dfr" = read_csv_and_map_dfr(), "fread & rbindlist" = fread_and_rbindlist(), "vroom" = my_vroom(), times = 30L ) compare autoplot(compare) + labs(title = "R 3.6.1")
以上。
R本体のアップデート時に入れるパッケージ
まず最初に必ず入れるパッケージ。
あとは必要になったときに入れれば大丈夫。
install.packages( c( "tidyverse", "data.table", "psych", "vegan", "plotly", "devtools", "colourpicker", # RStudio Addin "ggThemeAssist", # RStudio Addin "styler" # RStudio Addin ), Ncpus = getOption("Ncpus", 4L) )