data.table

[追記] 高速でデータを読み込む fread() 以外の選択肢としてHadley Wickhamの readr もお薦めです。現時点では fread() のほうが少し速いようです。

[2022年末追記] 2022年末、Rでcsvを読むという記事に最新事情が紹介されています。

data.tableとは?

data.table は従来の data.frame に代わるデータ型を提供するパッケージです。効率が良く,巨大なデータファイルの読み込み・書き出しが桁違いに速くなります。

インストールとライブラリの読み込みは想定通りです:

install.packages("data.table")
library(data.table)

データの読み込み

data.frame の読み込みは read.csv() などのいくつかの関数で行いましたが,data.table の読み込みは fread() だけです。

data = fread("ファイル名またはURL")

CSVかTSVか,ヘッダがあるかないかは,たいてい自動判断してくれます。もしうまくいかなかったら,オプションで指定します。

data = fread("ファイル名またはURLまたはデータ", sep=",", header=TRUE, na.strings=c("NA",""))

ただし,文字コードだけは設定できないようです。Mac上ではUTF-8しか扱ってくれませんでした。また,na.strings=c("","欠測値") のようなUTF-8文字列を与えても無視されるようです。

Sys.setlocale(category="LC_ALL", locale="ja_JP.SJIS") してみましたが,読み込み方は変わったものの,文字化けして表示されるだけのようです。

また,開発途上のためか高速化のためか,CSVはRFC 4180準拠ではなく,ダブルクォートが入り子になっているとうまくいきません。

おもしろい使い方として,data = fread(" まで打ってから,データをRコンソールにコピペし,") を打ち込むという方法も使えます。例えば

data = fread("魔,方,陣
2,9,4
7,5,3
6,1,8")

または

data = fread("
魔,方,陣
2,9,4
7,5,3
6,1,8
")

のような感じです。これで data と打ち込むと

   魔 方 陣
1:  2  9  4
2:  7  5  3
3:  6  1  8

と表示されます。

現実的な例でやってみましょう。RgoogleMapsを使った放射線地図で使った福島県の放射線データ fukushima.csv は,422656地点の緯度(lat),経度(lon),放射線(radiation,μSv/h単位),日時(datetime)を収めたものです。これをローカルにダウンロードして,読み込んで,それぞれの欄の平均値を表示し,時間を計測してみましょう(使っているマシンが多少速くなったので,結果を改訂しました):

> # 従来のdata.frame版
> system.time({df = read.csv("~/public_html/stat/data/fukushima.csv", as.is=TRUE)})
   ユーザ   システム       経過  
     0.928      0.027      0.963 
> system.time(print(mean(df$lat)))
[1] 37.59176
   ユーザ   システム       経過  
     0.001      0.000      0.001 
> system.time(print(mean(df$lon)))
[1] 140.4282
   ユーザ   システム       経過  
     0.001      0.000      0.001 
> system.time(print(mean(as.POSIXct(df$datetime))))
[1] "2011-07-18 01:52:49 JST"
   ユーザ   システム       経過  
     7.881      3.178     11.147 
> 
> # 新しいdata.table版
> system.time({dt = fread("~/public_html/stat/data/fukushima.csv")})
   ユーザ   システム       経過  
     0.174      0.022      0.057 
> system.time(print(mean(dt$lat)))
[1] 37.59176
   ユーザ   システム       経過  
     0.001      0.000      0.001 
> system.time(print(mean(dt$lon)))
[1] 140.4282
   ユーザ   システム       経過  
     0.001      0.000      0.001 
> system.time(print(mean(as.POSIXct(dt$datetime))))
[1] "2011-07-18 01:52:49 JST"
   ユーザ   システム       経過  
     7.890      3.093     11.059 

読み込みは劇的に早くなりました。他の計算はあまり変わりません。ただ,この場合は,日時を表す文字列を内部形式に変換する as.POSIXct() が遅すぎてどうにもなりません。

これを改善するには fasttime パッケージを使います。

install.packages("fasttime")

試してみましょう。

> library(fasttime)
> 
> system.time(print(mean(fastPOSIXct(df$datetime))))
[1] "2011-07-18 10:52:49 JST"
   ユーザ   システム       経過  
     0.022      0.001      0.024 
> system.time(print(mean(fastPOSIXct(dt$datetime))))
[1] "2011-07-18 10:52:49 JST"
   ユーザ   システム       経過  
     0.019      0.000      0.020 

今のマシンでも2桁以上,昔のマシンでは3桁高速化できました! すごいですね! でも答えが違ってますね。実は fasttime は変換元の文字列はGMTにしか対応していないので,JSTなら 9*3600 を引く必要があります。

> mean(fastPOSIXct(dt$datetime)) - 9*3600
[1] "2011-07-18 01:52:49 JST"

data.table の fread() と fasttime の fastPOSIXct() で劇的に高速化できることがおわかりいただけたでしょうか。

まだまだ未完ですが,とりあえず data.table を使った例:


[追記] lubridateパッケージにも高速な変換関数があります:R で文字列を POSIX time に変換するには lubridate::parse_date_time2() がちょっぱや #rstatsj - Qiita

[追記] Hadley Wickham が fread() に相当する readr というもの(旧 fastread)を作っています:hadley/readr · GitHub

[追記] readr がCRANに入りました(readr 0.1.0 | RStudio Blog)。SPSS,Stata,SASファイルを読む haven も。Excelファイルを読む readxl(RでExcelのデータを読む方法参照)も合わせて,Hadley Wickham だらけになりました。