読者です 読者をやめる 読者になる 読者になる

300億円欲しい

メジャーリーグのデータ解析します

効率的にデータフレームの処理がしたい(doByパッケージとは)

序論

Rでデータフレームを効率的に弄りたいです.
plyrパッケージはとても優秀です. 使いましょう.
doByパッケージもとても優秀です. 使いましょう.

irisデータの処理

データフレームを処理する道具として, plyrパッケージを紹介します.
みんな大好きirisデータで実験します.

> data(iris)
> head(iris)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa
> unique(iris$Species)
[1] setosa     versicolor virginica 
Levels: setosa versicolor virginica

irisは複数種の菖蒲に関するデータです.
Sepalはがく片, Petalは花びらという意味です.
irisデータには, それぞれの大きさと幅の数値が入っています.
Sepal.Length で菖蒲のがくの長さです.
最後の列に菖蒲の種類が記録されています.

このirisデータ. R界隈ではよく使われます.
このデータについて, 各種類別平均値が欲しい場合を考えます.
つまり, Speciesで分けて, それぞれの値を平均したものが知りたい場合を考えます.

apply関数を使う

大人なのでapplyを使います.

> iris_setosa <- subset(iris, iris$Species == "setosa")
> iris_setosa_mean <- sapply(iris_setosa[,1:4], mean)
> iris_setosa_mean
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
       5.006        3.428        1.462        0.246 

出ました.
残り2つの種類のアヤメで同様の処理を行って, 結果をまとめればいいですね.

plyrパッケージを使ってデータ処理

残り2つの種類のアヤメで同様の処理を行うのは面倒です.
最後に結果をまとめるのも面倒です.
全部自動的にやってくれる関数が欲しいです.

探せばあります. plyrパッケージです.
http://cran.r-project.org/web/packages/plyr/plyr.pdf

種類別に処理してから結果をまとめて出力してくれます. 今回はddplyを使います

> library(plyr)
> iris_mean_plyr <- ddply(iris, .(Species), # Speciesで分割する
+                         summarize, # 以下の計算をして, まとめて出力
+                         Sepal.Length = mean(Sepal.Length), 
+                         Sepal.Width = mean(Sepal.Width), 
+                         Petal.Length = mean(Petal.Length), 
+                         Petal.Width = mean(Petal.Width))
> iris_mean_plyr
     Species Sepal.Length Sepal.Width Petal.Length Petal.Width
1     setosa        5.006       3.428        1.462       0.246
2 versicolor        5.936       2.770        4.260       1.326
3  virginica        6.588       2.974        5.552       2.026

分割して, 処理して, まとめてくれました.

doByパッケージを使ってデータ処理.

もっと行数を減らしたいです. doByパッケージを使います.
これも, 分割して, 処理して, まとめてくれます.

> library(doBy)
> iris_mean_doBy <- summaryBy( . ~ Species,
+                               data = iris,
+                               FUN = mean,
+                               keep.names = TRUE)
> iris_mean_doBy
     Species Sepal.Length Sepal.Width Petal.Length Petal.Width
1     setosa        5.006       3.428        1.462       0.246
2 versicolor        5.936       2.770        4.260       1.326
3  virginica        6.588       2.974        5.552       2.026

分割して, 処理して, まとめてくれました.

MLBデータの処理で比較

plyrとdoBy. 今回は同じ処理が出来ました.
何が違うのでしょうかね. 処理速度ですか?
実験してみます. 野球のデータを使います.

メジャーリーグの2013年シーズンの全投球結果に関するデータが公開されています.
データをpitch f/xから持ってきます. 詳しくは前回参照
http://gg-hogehoge.hatenablog.com/entry/2013/12/21/075023

> data2013 <- read.csv("2013.csv")

1シーズン分で430MBあります. 中身を少し見てみます.

> head(data2013[,c("sv_id", "pitcher_name", "start_speed", "end_speed")])
            sv_id  pitcher_name start_speed end_speed
333 130222_130329 Derek Holland        93.2      86.2
334 130222_130341 Derek Holland        93.4      86.4
335 130222_130402 Derek Holland        84.3      76.9
336 130222_130444 Derek Holland        92.6      86.2
337 130222_130500 Derek Holland        82.8      77.0
338 130222_130529 Derek Holland        85.2      79.2

投手の名前と初速と終速がマイル表示で入っています.
先頭の行から,
2013年の2月22日の13時03分29秒に Derek Holland さん 93.2マイルの球を投げた
ことがわかります.
このデータで処理速度の調査をします.

全ての投球結果について, 直球の平均球速ランキングを作りたいです.
つまり, 投手の名前で分割して, 直球のデータについて平均を出してまとめます.
plyrとdoByが使えますね. 同じ処理をさせて, 速度を比較してみました.

> plyr_time
   user  system elapsed 
 16.303  12.998  30.019 
> doBy_time
   user  system elapsed 
  0.255   0.045   0.300 

意識が高いので見える化します.
f:id:gg_hatano:20131231154953p:plain

doByを使いましょう.

ついでにメジャーリーグの直球の速さランキングです.

> head(FF_start)
      pitcher_name start_speed end_speed
1     Bruce Rondon    159.7885  147.0118
2  Aroldis Chapman    158.1140  145.4615
3   Kelvin Herrera    158.0158  145.2006
4     Nathan Jones    157.2777  144.2170
5 Trevor Rosenthal    156.5369  143.4561
6      Duke Welker    156.4418  144.7118

2位のチャップマンは171キロまで出ます.

チャップマン 人類最速171キロ!! - YouTube

まとめ

doByとplyrはどうやって使い分けすればいいのでしょうか.
良くわからないので誰か教えてください.

ソースコード

data2013 <- read.csv("2013.csv")
data2013 <- subset(data2013, data2013$sv_id != "NA")
attach(data2013) 
start_speed <- as.numeric(start_speed)
end_speed <- as.numeric(end_speed)
detach(data2013)

data2013_FF <- subset(data2013, data2013$pitch_type=="FF")

library(plyr)
plyr_time <- system.time(FF_speed_mean <- ddply(data2013_FF, .(pitcher_name), summarize,
                         FF_start_speed = mean(start_speed),
                         FF_end_speed = mean(end_speed),
                         .progress = "text") 
                         )

library(doBy)
doBy_time <- system.time(FF_speed_mean2 <- summaryBy(start_speed + end_speed ~ pitcher_name, 
                                                     data = data2013_FF, 
                                                     FUN = mean, 
                                                     keep.names = TRUE))
plyr_time
doBy_time