巨大な表を高速に扱うData.table について

Preview:

Citation preview

14.07.02 Kashiwa.R #10

巨大な表を高速に扱うdata.table について

@yuifu

自己紹介• @yuifu

• 大学院生

• バイオインフォマティクス

http://www.slideshare.net/yuifu/fdr-kashiwar-3

data.table を紹介するスライドはすでに色々ありますが…

http://www.slideshare.net/sfchaos/datatable

(簡潔で分かりやすかったです)

でも、 data.table さんって data.frame さんとかなり違うの…

• data.frame で当たり前だと思ってたことが、当たり前じゃなかったり…

• そこで本発表では

• data.table を使おうとしたら data.frameと色々違ってて面倒だった点に重点を置いて紹介する

• データ解析でよく使われる

• 理解しやすい

イントロ

表には data.frame がよく使われる• 外部の表形式データを read.table, read.csv で読み込んだり

• R に用意されてるデータセットもたいてい data.frame

> class(USArrests)[1] "data.frame"> head(USArrests) Murder Assault UrbanPop RapeAlabama 13.2 236 58 21.2Alaska 10.0 263 48 44.5Arizona 8.1 294 80 31.0Arkansas 8.8 190 50 19.5California 9.0 276 91 40.6Colorado 7.9 204 78 38.7

イントロ

data.frame さんはちょっと…• data.frame で巨大な表を読み込むと…

• 読み込みが遅い• いちいち動作が重い• うっかり全行表示してしまうと辛い(固まる)

イントロ

遅くても待てばいい?

• ルーチンの作業を実行するならそうかもしれない

• データ解析だとルーチンでない作業が多い

• データ読み込んだり

• 加工したり

• 層別したり

• 集計したり

• →  対話的にデータをいじくるには速いに越したことはない

イントロ

そこで data.table

• 速い!

• 便利!

• うっかり全行表示しない!

http://photozou.jp/photo/show/1934405/188750786

イントロ

表の作成が速い

> grpsize = ceiling(1e8/26^2)> tt=system.time( DF <- data.frame(+ x=rep(LETTERS,each=26*grpsize),+ y=rep(letters,each=grpsize),+ v=runif(grpsize*26^2),+ stringsAsFactors=FALSE) + )> tt ユーザ システム 経過 18.319 9.352 35.238

> head(DF) x y v1 A a 0.52291182 A a 0.89705093 A a 0.63021304 A a 0.54247605 A a 0.21110726 A a 0.6619162

> grpsize = ceiling(1e8/26^2)> tt=system.time( DT <- data.table(+ x=rep(LETTERS,each=26*grpsize),+ y=rep(letters,each=grpsize),+ v=runif(grpsize*26^2)) + )> tt ユーザ システム 経過 6.866 0.980 8.787

> head(DT) x y v1: A a 0.43856272: A a 0.65080533: A a 0.79258304: A a 0.42202875: A a 0.84656196: A a 0.5719564

data.frame data.table

data.table の方が表の作成が速い!

data.tableの使い方

data.frame を data.table に変換

> dt_car <- data.table(cars)> head(dt_car) speed dist1: 4 22: 4 103: 7 44: 7 225: 8 166: 9 10

data.tableの使い方

表の読み込みが速い

> system.time(df <- read.table(filename, sep="\t", fill=T)) ユーザ システム 経過 7.472 0.176 7.738

> dim(df)[1] 626393 7

> system.time(dt <- fread(filename, sep="\t")) ユーザ システム 経過 0.387 0.028 0.416

> dim(dt)[1] 773042 7

data.frame data.table

data.table の方が表の読み込みが速い!

data.tableの使い方

全行表示しない

> dim(DT)[1] 100000004 3

> DT x y v1e+00: A a 0.43856272e+00: A a 0.65080533e+00: A a 0.79258304e+00: A a 0.42202875e+00: A a 0.8465619 --- 1e+08: Z z 0.27105131e+08: Z z 0.10012151e+08: Z z 0.71904531e+08: Z z 0.36776811e+08: Z z 0.4660653

data.tableの使い方

うっかり全行表示しようとしない!→ 安心!

tables() でメモリ上にある data.table オブジェクトを確認

> tables() NAME NROW MB COLS KEY [1,] ans2 147,929 4 x,y,v [2,] ans3 147,929 4 x,y,v x,y [3,] dt 773,042 40 method,V2,platform,source,V5,V6,year platform,method[4,] DT 100,000,004 2289 x,y,v x,y [5,] dt_car 50 1 speed,dist [6,] ss 26 1 x,V1 x Total: 2,339MB

data.tableの使い方

data.frame のようには要素にアクセスできない例

> dt method V2 platform source V5 V6 year 1: - - - - - - 2009 2: - - - - - - 2009 3: - - - - - - 2009 4: - - - - - - 2009 5: - - - - - - 2009 --- 773038: WGS Other unspecified GENOMIC - - 2012773039: WGS Other unspecified GENOMIC - - 2012773040: WGS Other unspecified GENOMIC - - 2012773041: WGS Other unspecified GENOMIC - - 2012773042: WGS Other unspecified GENOMIC - - 2012> dt[1,] method V2 platform source V5 V6 year1: - - - - - - 2009> dt[1,3][1] 3> dt[,3][1] 3

data.tableの使い方

[ ] の中の扱いが違う

• data.frame では、行と列の数字を入れる

> dt[i, j, ]

expression をいれる

key をいれる その他の引数を入れる

data.tableの使い方

i: 行へのアクセス

> dt x v1: b -1.079072452: b 0.925151703: b -0.023398634: a 2.064985475: a -1.03080796> dt[2,] x v1: b 0.9251517> dt[dt$x=="b",] x v1: b -1.079072452: b 0.925151703: b -0.02339863> dt["b",] 以下にエラー `[.data.table`(dt, "b", ) : When i is a data.table (or character vector), x must be keyed (i.e. sorted, and, marked as sorted) so data.table knows which columns to join to and take advantage of x being sorted. Call setkey(x,...) first, see ?setkey.

data.tableの使い方

i: キーによる行へのアクセス> setkey(dt, x)> tables() NAME NROW MB COLS KEY[1,] dt 5 1 x,v x [2,] dt_car 50 1 speed,dist Total: 2MB> dt["b",] x v1: b -1.079072452: b 0.925151703: b -0.02339863

data.tableの使い方

i: キーによる行へのアクセスは速い

> tt=system.time(+ ans1 <- DF[DF$x=="R" & DF$y=="h",])> tt ユーザ システム 経過 15.840 1.042 17.046

> tt=system.time(ans2 <- DT[DT$x=="R" & DT$y=="h",])> tt ユーザ システム 経過 6.756 0.368 7.124

data.frame data.table

> system.time(setkey(DT, x, y)) ユーザ システム 経過 0.688 0.178 0.871 > system.time(ans3 <- DT[J("R","h")]) ユーザ システム 経過 0.006 0.002 0.009

ベクトルを先頭からスキャンする場合

キーを設定し、二分探索を行う場合

キーを設定した方が速い!

data.tableの使い方

j: 集計する(例)

> DT[,sum(v)][1] 49994142

data.tableの使い方

> DT[,sum(v),by=x] x V1 1: A 1922034 2: B 1922008 3: C 1923013 4: D 1922349 5: E 1922904 6: F 1923302 7: G 1922370 8: H 1923551 9: I 192272710: J 192257311: K 192227612: L 192284313: M 192283714: N 192313715: O 192295816: P 192393717: Q 192278418: R 192324819: S 192278120: T 192296521: U 192416122: V 192255423: W 192300924: X 192265125: Y 192254326: Z 1922629 x V1

by で列名を指定すると指定した変数の値により行をグループ分けした上でsum(v) を行う

sum(v) を行う

j の位置では、列名は引用符で囲まない

> system.time(tt <- tapply(DT$v,DT$x,sum)) ユーザ システム 経過 35.287 14.561 56.807 > system.time(ss <- DT[,sum(v),by=x]) ユーザ システム 経過 1.704 0.441 2.395

tapply より速い!

j: expression を入れる

> dt_car[, plot(speed, dist)]NULL

> dt_car[, dist/speed] [1] 0.5000000 2.5000000 0.5714286 3.1428571 2.0000000 1.1111111 1.8000000 2.6000000 3.4000000 1.5454545[11] 2.5454545 1.1666667 1.6666667 2.0000000 2.3333333 2.0000000 2.6153846 2.6153846 3.5384615 1.8571429[21] 2.5714286 4.2857143 5.7142857 1.3333333 1.7333333 3.6000000 2.0000000 2.5000000 1.8823529 2.3529412[31] 2.9411765 2.3333333 3.1111111 4.2222222 4.6666667 1.8947368 2.4210526 3.5789474 1.6000000 2.4000000[41] 2.6000000 2.8000000 3.2000000 3.0000000 2.3478261 2.9166667 3.8333333 3.8750000 5.0000000 3.4000000

変数同士の計算ができたり

別の関数を実行したり

data.tableの使い方

data.frame と data.tabledata.tableの使い方

タスク data.table data.frame

表の作成 data.table data.frame

表の読み込み fread read.table

行の名前の設定 setkey rowname

列の名前の設定 setnames colnames

複数の表の結合 rbindlistcbind

rbindcbind

列の追加 :=cbind

data.frame

名前・発想が違うものについて

メモリ消費量はほぼ同じ

> dim(DF)[1] 100000004 3> object.size(DF)2400003488 bytes

> dim(DT)[1] 100000004 3> object.size(DT)2400004008 bytes

data.frame data.table

> system.time(setkey(DT, x, y)) ユーザ システム 経過 0.817 0.228 1.049 > object.size(DT)2400004272 bytes

10 億行 × 3列の表の場合

2つの列(変数)をキーに設定した後

速いのに、メモリ消費量はほぼ同じ!キーを設定した後でも、メモリ消費量はほぼ同じ!

実用例応用例

> dt <- fread("sra.list", sep=“\t")> setnames(dt, "V3", "platform")> setnames(dt, "V7", "year")> setnames(dt, "V1", "method")> setnames(dt, "V4", "source")> setkey(dt, source)> dat <- dt["GENOMIC", nrow(.SD), by=list(platform, source, year)]> dat <- dat[!grep("-|unsp", platform),]> library(ggplot2)> g <- ggplot(data.table(dat), aes(x=year, y=V1)) + + geom_bar(stat = "identity") ++ facet_wrap(~ platform) + + xlim(2008,2013) > print(g)

SRA で、 source がゲノム配列であるデータがよく読まれている platform は?

実用例応用例

> library(ggplot2)> g <- ggplot(data.table(dat), aes(x=year, y=V1)) + + geom_bar(stat = "identity") ++ facet_wrap(~ platform) + + xlim(2008,2013) > print(g)

まとめ

• 巨大な表は data.table で扱いましょう

参考文献

• CRAN の data.table パッケージのページ

• http://cran.r-project.org/web/packages/data.table/index.html

• 英語で詳しく書いてある

• R 言語上級ハンドブック

• 日本語で詳しく書いてある

Recommended