この記事は,「jupyter notebook Advent Calendar 2016」 12/15のネタとなります(といいつつ当日書けそうにないので前倒しで公開します).
先日(12/10)のPython mini Hack-a-thon(71回目)にて,
Jupyterとメジャーリーグ一球速報データを用いた一球速報っぽいモノを作る!
という目標を立てて色々とやっていて,一定の成果が出たので公開したいと思います.
Starting Member
- はじめに
- Who am I?
- 完成イメージ&コード
- 今回使ったモノ
- 【おさらい】野球のストライクゾーンについて
- Pitch f/xデータの座標系仕様(ざっくり)
- Jupyter + pandas + matplotlib(seaborn)で実装
- まとめ&次の方へ
- 【Appendix】今回,参考にした書籍
はじめに
この記事内の統一ルールです.
- MLB(メジャーリーグ)の事例かつ,日本のプロ野球はデータが手に入らないため再現不可能です.*1
- 距離(横幅・高さ・奥行き)はフィート(ft)単位です. ちなみに1フィートが約30.5cmです.
- 速度はマイル毎時(MPH, miles per hour)です. 時速にしたい方は1.6を掛けて読み替えてください.
Who am I?
完成イメージ&コード
いきなりですが結論からお見せします.
今回作ったモノ
今年(2016)の8/17 TEX-OAK戦の1回オモテ,ダルビッシュ有*2とダニー・バレンシア*3の打席の結果です.
これをJupyter(Python)とpandas, matplotlibを使って作ります.
なお, 主審(捕手)目線から見たプロットになるので要注意です!!!(≒パワプロと同じです)*4
念のため凡例を貼っておきます.
コード
ちょっとPythonを触れる方向けにサンプルコード(データ付属)を用意しました.
真似したい方はどうぞ&マー君とかマエケンとかココに無いデータでやりたい方は別途pitchpxでデータを取得してやってみてください(すぐ後の章で触れます).
サンプル(データ付き)
pitchpx(データセットの取得)
今回使ったモノ
プログラミング環境
OSは使い慣れたもので良いと思いますが,Linux系がやりやすいと思います.
- Python 3.5.2
- Jupyter(データ屋さん向けの統合環境)
- pandas(データフレームを操る有名過ぎるライブラリ)
- matplotlib(グラフ描画)
- seaborn(matplotlibの簡易版&ビジュアルをイカした奴にするモノ,今回は後者目的で利用)
サンプルコードをcloneした方は,以下のおまじないでインストールが完了します.
$ pip install -r requirements.txt
なお,今回はそれぞれのライブラリの解説はありません.
気になる方はググるか以下の書籍あたりを参考にされると良いかと思います.
Pythonエンジニア養成読本[いまどきの開発ノウハウ満載!] (Software Design plus)
IPythonデータサイエンスクックブック ―対話型コンピューティングと可視化のためのレシピ集
データ
Pitch f/x(高性能スピードガン)のデータを用います.
こちらについては,今年のPyCon JP 2016で紹介させてもらったpitchpxを用いて行います.
詳細については以下のスライドおよびQiitaの過去記事を御覧ください.
www.slideshare.net
この先はサンプルコードとデータが手元にある前提でお話を進めます.
【おさらい】野球のストライクゾーンについて
これであとはJupyter notebookを実行すれば出来上がり...なのですが,その前に野球のストライクゾーンの仕様についておさらいます.
なぜおさらいするかというと,
(趣味のものであれ)データ分析にはドメイン知識が必要だからです!
思わぬ落とし穴にはまらないよう,チェックしましょう.
とはいえ説明はこの絵一枚でおしまいです.
【図】ストライクゾーンの仕様
一言で言うと,
- ストライクゾーンの幅は固定
- ストライクゾーンの高さは可変,打者の体格によって変わる
です!
ちなみに今回は用いませんが,奥行きは固定(これもホームベースと同じ)です.
普段野球を見たりやったりしている人には常識ですが,あまりルールに慣れてない方は再確認しましょう.
Pitch f/xデータの座標系仕様(ざっくり)
ストライクゾーンの仕様が判明した所で,今回のデータの仕様を確認します.
例で上げたダルビッシュVSバレンシアの投球データの情報(データフレーム)はこんな感じです.
※必要なデータのみ抜粋しています
それぞれのカラムの意味は,本家アメリカに加え日本のブログでも紹介されています.(素晴らしい!!!)*5
具体的には,
- 本塁の場所を基準点とする
- 横軸(x)は本塁からの距離(px)を使う
- 縦軸(y)は本塁からの高さ(pz)を
- 残りのデータはラベル情報として使う
ということになります.
なお,ストライクゾーンは以下の用に定義しました.
- 横幅(固定)は基準点から0.8(-0.8)フィートの位置に定義.
- 厳密に言えば0.7(-0.7)ちょいですが, ホームベースの入り口から出口の流れを想定して敢えてマージンを取っています.
- 本家で似たような事をしているサイト(BrooksBaseball.net: Home of the PITCHf/x Tool)も同じ感じだったので真似した
- 高さ(可変)はsz_top(上限), sz_bot(下限)の平均値を使用.
- センサーデータなので実は投げる度に変わる
- とはいえ変わるの考慮するのはちょっとアレなので打席内の投球の平均で解決することに
座標データ
ラベル情報(実況データ)
サンプルコードからの抜粋&コメント付きで
# pitch type(変化球の種類) PITCH_TYPES = { 'CH': 'Change-up', 'CU': 'Curveball', 'EP': 'Ephuus', 'FA': 'Fastball', 'FC': 'Cut Fastball', 'FF': 'four-seam Fastball', 'FO': 'Forkball', 'FS': 'Split-finger Fastball', 'FT': 'two-seam Fastball', 'KC': 'Knuckle Curve', 'KN': 'Knuckleball', 'SC': 'Screwball', 'SI': 'Sinker', 'SL': 'Slider', 'UN': 'Unknown' } # pitched(一球毎のデータを回して突っ込む) ii = 1 for _, pitch in df_1_v.iterrows(): c = get_color(pitch['pitch_res']) marker = get_marker(pitch['pitch_type']) label = '{cnt}:{event}({type}, {speed}MPH)'.format( **{ 'cnt': ii, # 打席内のボールカウント 'speed': pitch['start_speed'], # 球速 'type': PITCH_TYPES.get(pitch['pitch_type'], 'UN'), # 球種 'event': pitch['pitch_des'], # 投球結果 } ) ax.scatter(pitch['px'], pitch['pz'], label=label, c=c, marker=marker) ii +=1
ここまで来たらあとは実装となります.
Jupyter + pandas + matplotlib(seaborn)で実装
あとはこのnotebookを元にうまくやってあげれば出てきます(雑)
ポイントを少しだけ解説します.
データの読み込み
pandasのread_csvで読んでおしまいです.
# read to datasets # TEX 6 - 2 OAK (2016/8/17) http://www.baseball-reference.com/boxes/TEX/TEX201608170.shtml df = pd.read_csv('./datasets/yu_darvish_201608170_pitch.csv')
絞り込み
1回のバレンシアの打席のみをクエリーで取ります.
カラムについては確認に必要なモノのみ.
# inning 1 Darvish VS Valencia(Strike out) # data df_1_v = df.query('inning_number==1 and bat_box_name=="Valencia"') df_1_v[['bat_box_name', 'px', 'pz', 'sz_top', 'sz_bot', 'pitch_type', 'pitch_res', 'start_speed', 'pitch_des']]
描画
matplotlibのscatterを使います.
見た目を固定するため,xlim/ylimの設定および,ストライクゾーンの定義(vlines/hlines)を行います.
あとは一球毎に挿して上げればOKです!(この辺もっと賢い書き方あったら教えて欲しい)
fig = plt.figure() ax = fig.add_subplot(1, 1, 1) ax.set_title('2016/8/17 inning 1 Darvish VS Valencia') ax.set_xlabel('px') ax.set_xlim((-3.0, 3.0)) ax.set_ylabel('pz') ax.set_ylim((0.0, 5.0)) ax.grid(True) # Strike Zone ax.vlines(-0.8, ymin=1.58, ymax=3.45) ax.vlines(0.8, ymin=1.58, ymax=3.45) ax.hlines(1.58, xmin=-0.8, xmax=0.8) ax.hlines(3.45, xmin=-0.8, xmax=0.8) # pitched ii = 1 for _, pitch in df_1_v.iterrows(): c = get_color(pitch['pitch_res']) marker = get_marker(pitch['pitch_type']) label = '{cnt}:{event}({type}, {speed}MPH)'.format( **{ 'cnt': ii, 'speed': pitch['start_speed'], 'type': PITCH_TYPES.get(pitch['pitch_type'], 'UN'), 'event': pitch['pitch_des'], } ) ax.scatter(pitch['px'], pitch['pz'], label=label, c=c, marker=marker) ii +=1 ax.legend()
まとめ&次の方へ
まとめ
- bokehあたりと組み合わせてカッコイイ一球速報を作れそう!
- この辺もライブラリ化して可視化を楽にしたくなってきた
- 実験しやすいJupyterはやっぱ便利
- 【反省点】キャッチャー視点での打席勝負がみられない
次の方
どりらんさん,よろしくお願いします!
【Appendix】今回,参考にした書籍
Pythonと野球方面で.
Python
Pythonエンジニア養成読本[いまどきの開発ノウハウ満載!] (Software Design plus)
- 作者: 鈴木たかのり,清原弘貴,嶋田健志,池内孝啓,関根裕紀,若山史郎
- 出版社/メーカー: 技術評論社
- 発売日: 2015/04/17
- メディア: 大型本
- この商品を含むブログ (1件) を見る
IPythonデータサイエンスクックブック ―対話型コンピューティングと可視化のためのレシピ集
- 作者: Cyrille Rossant,菊池彰
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/12/25
- メディア: 大型本
- この商品を含むブログ (1件) を見る
野球
Analyzing Baseball Data with R (Chapman & Hall/CRC The R Series)
- 作者: Max Marchi,Jim Albert
- 出版社/メーカー: Chapman and Hall/CRC
- 発売日: 2016/04/05
- メディア: Kindle版
- この商品を含むブログを見る
公認野球規則〈2016〉Official Baseball Rules
- 作者: 日本プロフェッショナル野球組織,全日本野球協会
- 出版社/メーカー: ベースボールマガジン社
- 発売日: 2016/04
- メディア: 単行本
- この商品を含むブログ (1件) を見る