14.07.02 Kashiwa.R #10
巨大な表を高速に扱う
data.table について
@yuifu
自己紹介
• @yuifu
• 大学院生
• バイオインフォマティクス
2
http://www.slideshare.net/yuifu/fdr-kashiwar-3
data.tableを紹介するスライドはすでに色々ありますが…
3
http://www.slideshare.net/sfchaos/datatable
(簡潔で分かりやすかったです
)
でも、data.tableさんってdata.frameさんとかなり違うの…
• data.frameで当たり前だと思ってたことが、当たり前じゃなか
ったり…
• そこで本発表では
• data.tableを使おうとしたらdata.frameと色々違ってて面倒
だった点に重点を置いて紹介する
4
表
• データ解析でよく使われる
• 理解しやすい
7
イントロ
表にはdata.frameがよく使われる
• 外部の表形式データをread.table, read.csvで読み込んだり…
• Rに用意されてるデータセットもたいていdata.frame
8
> class(USArrests)
[1] "data.frame"
> head(USArrests)
Murder Assault UrbanPop Rape
Alabama 13.2 236 58 21.2
Alaska 10.0 263 48 44.5
Arizona 8.1 294 80 31.0
Arkansas 8.8 190 50 19.5
California 9.0 276 91 40.6
Colorado 7.9 204 78 38.7
イントロ
data.frameさんはちょっと…
• data.frameで巨大な表を読み込むと…
• 読み込みが遅い
• いちいち動作が重い
• うっかり全行表示してしまうと辛い(固まる)
9
イントロ
遅くても待てばいい?
• ルーチンの作業を実行するならそうかもしれない
• データ解析だとルーチンでない作業が多い
• データ読み込んだり
• 加工したり
• 層別したり
• 集計したり
• → 対話的にデータをいじくるには速いに越したことはない
10
イントロ
そこでdata.table
• 速い!
• 便利!
• うっかり全行表示しない!
11
http://photozou.jp/photo/show/1934405/188750786
イントロ
表の作成が速い
14
> 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 v
1 A a 0.5229118
2 A a 0.8970509
3 A a 0.6302130
4 A a 0.5424760
5 A a 0.2111072
6 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 v
1: A a 0.4385627
2: A a 0.6508053
3: A a 0.7925830
4: A a 0.4220287
5: A a 0.8465619
6: A a 0.5719564
data.frame data.table
data.tableの方が表の作成が速い!
data.tableの使い方
data.frameをdata.tableに変換
15
> dt_car <- data.table(cars)
> head(dt_car)
speed dist
1: 4 2
2: 4 10
3: 7 4
4: 7 22
5: 8 16
6: 9 10
data.tableの使い方
表の読み込みが速い
16
> 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の使い方
全行表示しない
17
> dim(DT)
[1] 100000004 3
> DT
x y v
1e+00: A a 0.4385627
2e+00: A a 0.6508053
3e+00: A a 0.7925830
4e+00: A a 0.4220287
5e+00: A a 0.8465619
---
1e+08: Z z 0.2710513
1e+08: Z z 0.1001215
1e+08: Z z 0.7190453
1e+08: Z z 0.3677681
1e+08: Z z 0.4660653
data.tableの使い方
うっかり全行表示しようとしない!
→安心!
tables()でメモリ上にあるdata.tableオブジェクトを確認
18
> 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のようには要素にアクセスできない例
19
> dt
method V2 platform source V5 V6 year
1: - - - - - - 2009
2: - - - - - - 2009
3: - - - - - - 2009
4: - - - - - - 2009
5: - - - - - - 2009
---
773038: WGS Other unspecified GENOMIC - - 2012
773039: WGS Other unspecified GENOMIC - - 2012
773040: WGS Other unspecified GENOMIC - - 2012
773041: WGS Other unspecified GENOMIC - - 2012
773042: WGS Other unspecified GENOMIC - - 2012
> dt[1,]
method V2 platform source V5 V6 year
1: - - - - - - 2009
> dt[1,3]
[1] 3
> dt[,3]
[1] 3
data.tableの使い方
[ ] の中の扱いが違う
• data.frameでは、行と列の数字を入れる
20
> dt[i, j, ]
expressionをいれる
keyをいれる その他の引数を入れる
data.tableの使い方
i: 行へのアクセス
21
> dt
x v
1: b -1.07907245
2: b 0.92515170
3: b -0.02339863
4: a 2.06498547
5: a -1.03080796
> dt[2,]
x v
1: b 0.9251517
> dt[dt$x=="b",]
x v
1: b -1.07907245
2: b 0.92515170
3: 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: キーによる行へのアクセス
22
> 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 v
1: b -1.07907245
2: b 0.92515170
3: b -0.02339863
data.tableの使い方
i: キーによる行へのアクセスは速い
23
> 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: 集計する(例)
24
> 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 1922727
10: J 1922573
11: K 1922276
12: L 1922843
13: M 1922837
14: N 1923137
15: O 1922958
16: P 1923937
17: Q 1922784
18: R 1923248
19: S 1922781
20: T 1922965
21: U 1924161
22: V 1922554
23: W 1923009
24: X 1922651
25: Y 1922543
26: 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を入れる
25
> 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.table
26
data.tableの使い方
タスク data.table data.frame
表の作成 data.table data.frame
表の読み込み fread read.table
行の名前の設定 setkey rowname
列の名前の設定 setnames colnames
複数の表の結合
rbindlist
cbind
rbind
cbind
列の追加 :=
cbind
data.frame
名前・発想が違うものについて
メモリ消費量はほぼ同じ
27
> 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つの列(変数)をキーに設定した後
速いのに、メモリ消費量はほぼ同じ!
キーを設定した後でも、メモリ消費量はほぼ同じ!
実用例
34
応用例
> 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は?
実用例
35
応用例
> 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で扱いましょう
36
参考文献
• CRANのdata.tableパッケージのページ
• http://cran.r-project.org/web/packages/data.table/index.html
• 英語で詳しく書いてある
• R言語上級ハンドブック
• 日本語で詳しく書いてある
37

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