野球データを速く集計したい (R vs nysol)
はじめに
R advent calendar, 12月10日分です.
nysolを使ってみた, という話です.
速度比較の記事を見ると, nysol使いたくなりますよね.
使ってみたら案外イケました. コードも書きやすいです.
野球データ解析のコードを例に, plyr, dplyr, nysolのコードと処理速度を比較してみました.
メジャーリーグ: 大チャンスでの得点率
メジャーリーグで, 犠牲フライでも点が入る場面で, ちゃんと得点を挙げてくれる選手を調べます.
2013年メジャーリーグの全打席結果データ(77MB)を利用して, 集計してみます.
データはretrosheetからダウンロード出来ますし, githubにもあります. .
集計は, 以下の3つの方法で行います.
-
data.frame + plyr
-
data.table + dplyr
-
nysol
集計からランキング表示まで, どの方法が一番速いでしょうか.
data.frame + plyrで集計
何も工夫せずに書いている感じです. 去年の私の知識で頑張ったのでしょう.
start = proc.time()
library(plyr)
library(stringr)
# データ読み込み
read_time = system.time(data2013 <- read.csv("all2013.csv"))
# 打者結果だけ
data2013 <- subset(data2013, BAT_EVENT_FL == TRUE)
# 大チャンス場面かどうかをSuperChanceに入れる
data2013$SuperChance <- with(data2013, ifelse(OUTS_CT<2 & BASE3_RUN_ID!="" , 1,0 ))
# 大チャンス場面で点が入ったかどうかをSuperChanceScoredに入れる
data2013$SuperChanceScored <- with(data2013, ifelse(OUTS_CT<2 & BASE3_RUN_ID!="" & str_detect(EVENT_TX,"3-H"), 1,0 ))
# ddplyで要約
data2013.SuperChance <- ddply(data2013, .(BAT_ID), summarize,
SuperChance = sum(SuperChance),
SuperChanceScored = sum(SuperChanceScored),
SuperChanceScoredRate = SuperChanceScored/SuperChance)
# 10打席以上
data2013.SuperChance.over10chance <- subset(data2013.SuperChance, SuperChance>10)
# 得点ゲット率で並び替え
data2013.SuperChance.over10chance.arranged <- arrange(data2013.SuperChance.over10chance, desc(SuperChance))
# ランキング
head(data2013.SuperChance.over10chance.arranged, 10)
## BAT_ID SuperChance SuperChanceScored SuperChanceScoredRate
## 1 philb001 65 39 0.6000000
## 2 brucj001 54 30 0.5555556
## 3 pedrd001 54 33 0.6111111
## 4 vottj001 53 21 0.3962264
## 5 fielp001 50 27 0.5400000
## 6 mccua001 50 22 0.4400000
## 7 canor001 49 28 0.5714286
## 8 napom001 47 21 0.4468085
## 9 andre001 46 24 0.5217391
## 10 belta001 46 21 0.4565217
end = proc.time()
処理時間
## 全体の処理時間
end - start
## user system elapsed
## 21.422 0.570 22.051
## データ読み込み時間
read_time
## user system elapsed
## 18.810 0.385 19.238
読み込みに19.238秒, 全体の処理時間が22.051秒.
大半がデータ読み込み時間だ, ということでした.
subsetとかddplyとか, 久しぶりに見ました. 2度と使わないと思います.
data.table + dplyrで集計
freadで読み込んでdplyrで集計しましょう.
start2 = proc.time()
library(data.table)
library(dplyr)
library(stringr)
## データ読み込み
read_time2 = system.time(data2013 <- fread("all2013.csv"))
## 大チャンスに強い打者ランキング作成
data2013 %>%
filter(BAT_EVENT_FL=="T") %>%
mutate(SUPER_CHANCE=ifelse((OUTS_CT<2 & BASE3_RUN_ID!=""), 1, 0)) %>%
mutate(SUPER_CHANCE_SCORED =
ifelse(OUTS_CT<2 & BASE3_RUN_ID!="" & str_detect(EVENT_TX,"3-H"), 1, 0)) %>%
group_by(BAT_ID) %>%
summarise(SUPER_CHANCE=sum(SUPER_CHANCE),
SUPER_CHANCE_SCORED = sum(SUPER_CHANCE_SCORED)) %>%
mutate(SUPER_CHANCE_SCORED_RATE = SUPER_CHANCE_SCORED/SUPER_CHANCE) %>%
filter(SUPER_CHANCE>10) %>%
arrange(desc(SUPER_CHANCE_SCORED_RATE))
## Source: local data table [340 x 4]
##
## BAT_ID SUPER_CHANCE SUPER_CHANCE_SCORED SUPER_CHANCE_SCORED_RATE
## 1 robeb003 15 14 0.9333333
## 2 blank002 11 10 0.9090909
## 3 bourm001 20 17 0.8500000
## 4 reyej001 12 10 0.8333333
## 5 peres002 21 17 0.8095238
## 6 suzuk001 15 12 0.8000000
## 7 lombs002 14 11 0.7857143
## 8 utlec001 29 22 0.7586207
## 9 carpm001 20 15 0.7500000
## 10 encae001 20 15 0.7500000
## .. ... ... ... ...
end2 = proc.time()
処理時間
## 全体の処理時間
end2 - start2
## user system elapsed
## 3.348 0.343 3.791
## データ読み込み時間
read_time2
## user system elapsed
## 1.105 0.078 1.219
読み込みに1.219秒, 全体の処理時間が3.791秒.
だいぶ速くなりました.
nysolで集計
# 処理に使うコード
cat ./superChance.bash
## #!/bin/bash
##
## cat ./all2013.csv |
## mselstr f=BAT_EVENT_FL v=T |
## mcal c='if(${OUTS_CT}<2 && $s{BASE3_RUN_ID}!="", 1, 0)' a="SUPER_CHANCE" |
## mcal c='if(${OUTS_CT}<2 && $s{BASE3_RUN_ID}!="" && regexs($s{EVENT_TX}, "3-H"),1, 0)' \
## a="SUPER_CHANCE_SCORED" |
## mcut f=BAT_ID,SUPER_CHANCE,SUPER_CHANCE_SCORED |
## msum k=BAT_ID f=SUPER_CHANCE,SUPER_CHANCE_SCORED |
## mcal c='${SUPER_CHANCE_SCORED}/${SUPER_CHANCE}' a="SUPER_CHANCE_SCORED_RATE" |
## msel c='${SUPER_CHANCE}>10' |
## msortf f=SUPER_CHANCE_SCORED_RATE%nr |
## head -n 10
dplyrで書いたコードと, ほとんど同じ頭の使い方で書けます.
処理時間を測ってみます.
# 処理時間の計測
time ./superChance.bash
## BAT_ID,SUPER_CHANCE,SUPER_CHANCE_SCORED,SUPER_CHANCE_SCORED_RATE%0nr
## robeb003,15,14,0.9333333333
## blank002,11,10,0.9090909091
## bourm001,20,17,0.85
## reyej001,12,10,0.8333333333
## peres002,21,17,0.8095238095
## suzuk001,15,12,0.8
## lombs002,14,11,0.7857142857
## utlec001,29,22,0.7586206897
## encae001,20,15,0.75
##
## real 0m1.950s
## user 0m2.134s
## sys 0m0.345s
あまり変わらないような... データが数10MBですからね. こんなもんかと.
ここまでのまとめ
nysolが1番速いですが, そこまで性能差を感じません.
データの大きさに依存すると思います.
10MBオーダーなら, data.table::freadしてdplyrでいいと思いました.
追加実験: 野球データ(80年分:4.3GB)を集計したい
これまでは2013年の結果データ(77MB)を使っていました.
データを大きくして, 同じ処理を行ってみます.
1921年から2013年までのメジャーリーグ打席結果データを全て繋げて, 集計してみます. 4.3GBあります. ビッグデータです.
コードは同じです. 結果だけ載せます. 手元では, こうなりました.
-
data.frame + plyr 実行不可能
-
data.table + dplyr 20分
-
nysol 1分16秒
なるほど. nysol. 凄い.
まとめ
100MBを超えたくらいから, Rへのデータ読み込みがキツくなってくると思います.
そこでnysol. dplyrのノリでなかなか自由に書けて楽しいです.
データが大きくになるにつれて, nysolが強くなっていきます.
nysolのmコマンドを覚えておいて, 状況に応じてRと使い分けていくといいかな, と思いました.
以上です.