この記事について
ちょっと大きなcsvファイルを読み込むのに、data.table::fread
がすごく速かったとつぶやいたら(data.table::freadが速い)、いくつかリツイートしてもらったので、その後の話を少し公開してみる。data.table::fread
やreadr::read_csv
は確かに速いけど、utils::read.csv
と同じ結果を求めるためには、ちょっと一手間いりますよという話です。
(追記1:日付忘れました)@r-de-rさまからコメントをいただき、Macでは同じようにならない旨、ご指摘いただきました。参考までにたけしょうがこの記事を書いたときの実行環境を記しておきます。
- Windows 7 SP1
- R 3.2.2
- data.table 1.9.6
- readr 0.2.2
(追記2:20151111)@yutannihilationさまがいろいろと調べて下さいました。この記事の内容はパッケージのバージョンによって結果が左右されるようです。すごく魅力的なパッケージですが、まだ開発も続いているパッケージですし、ある程度落ち着くまでは、積極的な利用は控えていた方がいいかもしれません(コードが後々使えなくなる可能性があるので)。
読み込みの検証
同じデータを読み込んで、同じ結果になるまでの手順を探ります。
読み込みデータについて
ファイルは適当に作ったcsvファイル(5KB程度)を利用します。
utils::read.csv
みんな大好きutils::read.csv
です。最初、データの読み込みには、この関数を使えと教わる人が多いことでしょう(utils::read.table
含む)。以下の例では、na.strings = ""
として、空白をNAとして扱う指定をしています。
dat1 <- read.csv("http://plaza.umin.ac.jp/~takeshou/R/dat/survey.csv",
na.strings = "")
str(dat1)
'data.frame': 100 obs. of 10 variables:
$ sample : int 1 2 3 4 5 6 7 8 9 10 ...
$ age : int 60 28 23 46 59 43 56 64 45 52 ...
$ height : num 185 155 171 163 163 ...
$ weight : num 86.7 57.8 66.1 62.4 60.4 ...
$ sex : Factor w/ 2 levels "f","m": 1 2 2 1 1 2 2 1 2 2 ...
$ village : Factor w/ 3 levels "A","B","C": 2 1 1 1 3 1 1 3 2 1 ...
$ income : num 8.72 13.61 9.2 6.9 11.18 ...
$ disease : int 1 0 0 0 1 0 0 0 1 0 ...
$ treatment: Factor w/ 2 levels "X","Y": 1 NA NA NA 2 NA NA NA 2 NA ...
$ outcome : int 1 NA NA NA 1 NA NA NA 1 NA ...
utils::read.csv
のdefaultでは、文字列は因子になって付置されます。医学・保健系の解析を行う場合では、ほとんどの場合、この動作で問題はないと思いますし、この動作が最も便利だと思います。1
ですので、今回は、このdat1
と同じモノをdata.table::fread
とreadr::read_csv
を使って作成してみましょう。
data.table::fread
後述のreadr::read_csv
もそうですが、defaultでは、文字列が文字列として読み込まれます。なので前提として、stringsAsFactors = TRUE
というオプション指定が必要です。
library("data.table")
dat2 <- fread("http://plaza.umin.ac.jp/~takeshou/R/dat/survey.csv",
stringsAsFactors = TRUE,
na.strings = "")
str(dat2)
Classes ‘data.table’ and 'data.frame': 100 obs. of 10 variables:
$ sample : int 1 2 3 4 5 6 7 8 9 10 ...
$ age : int 60 28 23 46 59 43 56 64 45 52 ...
$ height : num 185 155 171 163 163 ...
$ weight : num 86.7 57.8 66.1 62.4 60.4 ...
$ sex : Factor w/ 3 levels NA,"f","m": 2 3 3 2 2 3 3 2 3 3 ...
$ village : Factor w/ 4 levels NA,"A","B","C": 3 2 2 2 4 2 2 4 3 2 ...
$ income : num 8.72 13.61 9.2 6.9 11.18 ...
$ disease : int 1 0 0 0 1 0 0 0 1 0 ...
$ treatment: Factor w/ 3 levels NA,"X","Y": 2 1 1 1 3 1 1 1 3 1 ...
$ outcome : int 1 NA NA NA 1 NA NA NA 1 NA ...
- attr(*, ".internal.selfref")=
このままでは、data.table
としての属性も持ちますので、as.data.frame
を使って、data.frame
に変換しますが、実は、Factor
のところでNAも水準の1つとしてカウントされています。これはfactor
を使って、調整します。
dat2 <- as.data.frame(dat2)
for(loop in seq(along.with = dat2)){
if(is.factor(dat2[, loop])){
dat2[, loop] <- factor(dat2[, loop])
}
}
ここまでやって、漸くdat1
とdat2
で同じものになりました。
identical(dat1, dat2)
[1] TRUE
readr::read_csv
readr::read_csv
もほぼ同様ですが、今度は問題が2つになります。1つめは、文字列を因子に変換してくれないことで、2つめは、文字コードの問題です。僕は基本的にWindows環境ですので、最初、SJISを指定せずに読み込んだとき、結構悲惨な状態になりました。
library(readr)
dat3 <- read_csv("http://plaza.umin.ac.jp/~takeshou/R/dat/survey.csv",
locale = locale(encoding = "SJIS"))
str(dat3)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 100 obs. of 10 variables:
$ sample : int 1 2 3 4 5 6 7 8 9 10 ...
$ age : int 60 28 23 46 59 43 56 64 45 52 ...
$ height : num 185 155 171 163 163 ...
$ weight : num 86.7 57.8 66.1 62.4 60.4 ...
$ sex : chr "f" "m" "m" "f" ...
$ village : chr "B" "A" "A" "A" ...
$ income : num 8.72 13.61 9.2 6.9 11.18 ...
$ disease : int 1 0 0 0 1 0 0 0 1 0 ...
$ treatment: chr "X" NA NA NA ...
$ outcome : int 1 NA NA NA 1 NA NA NA 1 NA ...
最初にdata.frame
に変換し、次にcharacter
からfacto
rへの変換を行います。
dat3 <- as.data.frame(dat3)
for(loop in seq(along.with = dat3)){
if(is.character(dat3[, loop])){
dat3[, loop] <- factor(dat3[, loop])
}
}
identical(dat1, dat3)
[1] TRUE
これで漸くutils::read.csv
と同等の結果が得られました。
以上でこのつぶやきの全貌の紹介を終わります。
読み込みスピード
今回は、小さなファイルの読み込みですので、スピードについては検証していません。同じ結果を得られるためのスピードも誰かが…
終わりに
初めてQiitaに書いてみました。こういう情報共有サービスというのはありがたいですね。なるべく間違いのないように記述したつもりですが、何かございましたら、なんなりとご指摘いただけますと助かります。
-
この後、diseaseやoutcomeも因子に変更しますが、今回はそこまでは無視します。 ↩