Lean Baseball

No Engineering, No Baseball.

【野球Hack】エースの調子が悪くて心配なのでPython+pandas+matplotlibで可視化してみた

f:id:shinyorke:20150504144942p:plain

一ヶ月ぶり、こちらのエントリーの続編です。

shinyorke.hatenablog.com

こちらで作ったデータですが、その後も粛々とデータの分析に使ったり追加要件の開発を行ったりしてる訳ですが*1、つい最近このデータを使って新しいネタを作ったので簡単に紹介します。

Python+pandas+matplotlibで先発エースの勝ち星を月別で集計&グラフ化してみた

  • キッカケとテーマ
  • はじめてのpandas
  • matplotlibで可視化
  • 結論
  • 【Appendix】参考文献

キッカケとテーマ

私は現実の野球もゲームの野球も大好きで、時間を見つけては一球速報やらオンデマンドで中継を見てるのですが*2、ある日こんなことに気が付きました。

  • シカゴ・カブスのエース左腕、ジョン・レスター*3投手の4月成績が酷い。4試合投げて0勝2敗防御率6.23とは何事だ!
  • しかし、レスターはここ5年安定した成績を残している投手、このまま終わるとも思えない。
  • 【もしかして】春が苦手で毎年4~6月までは調子悪いのでは?

私はカブスのファンではないのですが、去年の夏にアスレチックスに移籍、プレーオフ進出に一役買った左腕が調子悪いことは心配していました。

また、そんな縁で(?)、ファンタジーベースボールでレスターを保有しており、我がチームのエースである彼がこのままだと色々困るので、本気を出して調べてみることにしました。

まずは単純に過去五年(2010~2014)の月ごとの勝利数・敗北数を調査、これらを見やすくグラフに書く所から始めました。

はじめてのpandas

最初はそのままMySQLにクエリー発行してデータ取ってグラフを書く、、、という感じでやろうと思ってましたが、そういえばちゃんとpandas使ったこと無いや、せっかくの機会だしちゃんとやってみよう!と思いpandasを使って前処理っぽい事をすることにしました。

Retrosheet(MySQL)のデータを読んでデータフレームにして返すコード

参照用の選手情報(Roster)、そして試合の結果(Games)をデータフレームとして保持するクラスを書きました。

テーブルの全行全列を引っこ抜く非常にシンプルな実装となっていますw(後続手順でサマリーします)

ちなみに、打席情報(Events)は今回使っていません。勝利数とか敗北数を取るだけなら試合情報だけで済むので。*4

コードの実行結果はこんな感じです。

試合情報は全て取れてるっぽいです。

f:id:shinyorke:20150504013335p:plain

選手情報も使えるかチェックしてみました。

ちゃんとIchiro Suzuki選手の情報が拾えています。

f:id:shinyorke:20150504013447p:plain

matplotlibで可視化

とまあこんな感じで使える目処がたったので、

  • ジョン・レスター投手の2010~2014シーズンの各月毎の勝利数をデータフレームとして出す
  • 出したデータフレームを折れ線グラフに描く

という実装を行いました。

まずは各年各月の勝利数(敗北数)を出すコードはこちら。

Pitcherの成績をデータフレーム化して返すクラス

先ほどのクラス(retrosheet_analytics.py)で引いてきたgamesにたいしてフィルターをかけて行を選択、新しいデータフレームを作って返しています。

で、このクラスにレスターのplayer_id(Retrosheetの選手ユニークキー)を渡してデータフレームを取ってみます。

今回も手元でサクサク試す目的でIPython notebookを活用しました。

f:id:shinyorke:20150504145032p:plain

行は月(3~10月)月を、列は年(2010~2014)を表しています。

各列の足し算の結果=レスターの各年の勝利数と同じになっていれば正解なのですが、残念ながら2013年だけズレています。です!!!*5

他の投手で調べても2013年だけズレているので、これは元データもしくはMySQLデータを作った時の実装(前のエントリー)に問題がある可能性が高いです。

※結局データが原因でした(5/3時点、現在は解消済み)

でも今回はまずはグラフに描く所までやろう!なんか上手く行ってる感あるね!、という事でmatplotlibで書いてみました。

f:id:shinyorke:20150504144942p:plain

データがおかしい2013年を除き、

  • 毎年7月はほとんど勝てない、というかゴミ
  • その次に勝てないのが4,9月だが謎に9月に強い時もアリ
  • 5,6,8月は安定して勝ってる、特に5月は無双してる
  • 2013年のデータが他のシーズンと違う傾向をみせている(4月に強くて他の月も安定)。ちなみにこの年は当時在籍していたボストン・レッドソックスが世界一になったシーズンで、チーム的に色々噛み合っていたのかもしれない(いい意味で)。

という特徴が見えます。やっぱグラフ見やすいですね!。

ちなみに、敗北数のデータフレーム&グラフはこちら!

f:id:shinyorke:20150504145216p:plain

通算成績117勝69敗、通算勝率.629(2014/5/4現在)を誇るレスターらしく、多い月でも3回程度しか負けていません。

セイバーメトリクス(野球統計学)の世界では勝利と敗戦はあまり意味がないのですが、やっぱ負けない投手って気持ちいいですね!

結論

例年の傾向が当てはまるのであれば、

  • 4月はベンチで寝かしておくのが正解(事前に知っていれば...悔しいorz)
  • 5,6月は絶頂期!ガンガン使うべき!!

という仮説が成立します(ただし野球ゲームに限る*6)。

と、いう訳でこの傾向が正しければレスターは5月6月、カブス(と我がチーム)の為にたくさん勝ってくれる!という事になります!

なお、それを表すかのように、5月最初の試合(5/1ブリュワーズ戦)で7回無失点被安打わずか3本という圧巻の投球で無事今季初勝利をゲットしています。

ただ、この仮説、ちょっと自信ない所もあって、

  • 過去5年はすべてDH制があるアメリカンリーグ、それもほぼ同一チーム(レッドソックス)の記録
  • 例年4月が悪いとはいえ、0勝は流石に悪すぎ
  • 【もしかして】去年の好成績(16勝11敗防御率2.46)の反動が今年に!?

DH制が無いナショナルリーグ、新天地のカブスで成績が揺らぐ可能性がありますし、レスターも徐々にベテランになりつつあるので、成績下降も予想されます。

この編の傾向がどうなるか、シーズン終わったあとにちゃんと振り返りたいものです。

あと、Retrosheetデータベースのバグ(2013年データが怪しい)も見つかったので、こちらもちゃんと対処したいと思います。

【Appendix】参考文献

pandas + matplotlibについてはこちらを参照しながらアレンジして書きました。

pandas

10分で出来る!、、、は10分じゃ出来ないっす量的にw

でも凄く役に経ちました!

matplotlib

pandas + matplotlib全般

月並みではございますが、こちらの本の「PyData入門」が役立ちました!!!(@iktakahiroさんGJ!!!)

Pythonエンジニア養成読本[いまどきの開発ノウハウ満載!] (Software Design plus)

Pythonエンジニア養成読本[いまどきの開発ノウハウ満載!] (Software Design plus)

最近は仕事でちゃんとPython使ってないので、要点と事例がまとまってる文章は本当助かりまっす。

Pythonをはじめたい皆さんも中級者以上の方も、養成読本メッチャいいですよ!(宣伝)

おやすみなさい。

*1:PyCon JP 2015の準備および、本気で野球ゲームに勝つための経営分析も兼ねています(真顔)

*2:MLB at Batマジ最強。Apple Watch版も思ったより使えてびっくり。

*3:元々はボストン・レッドソックスのエース左腕。昨年夏までボストンに在籍、PO進出かかって先発が欲しかったアスレチックスとシーズンを諦めかけていたレッドソックスの思惑が一致、主砲セスペデス他との大型トレードでアスレチックスに移籍。シーズン終了後FAとなり、かなりの大型契約でシカゴ・カブスに移籍した。

*4:防御率やWHIPSを出す、打者との相性云々までみるのであれば必要なのですが、今回はそこまで手を付けませんでした(あまり意味が無いため)。

*5:記事を書いた5/3時点ではズレていましたが、データのバグが発覚、修正して無事通りました^^よってスクショとかも差し替えております...

*6:故障もしくは深刻なスランプ・衰えが無い限り、現実の野球では投げさせるべきです、念のため