Lean Baseball

Engineering/Baseball/Python/Agile/SABR and more...

【野球Hack】PythonとJupyterで「一球速報」っぽいモノを作る(MLB編) #pyhack

この記事は,「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?

  • shinyorke(しんよーく、と読みます)
  • Webとデータ,ちょっとだけアジャイルなエンジニア.
  • Pythonで野球の人」らしいです

完成イメージ&コード

いきなりですが結論からお見せします.

今回作ったモノ

今年(2016)の8/17 TEX-OAK戦の1回オモテ,ダルビッシュ有*2とダニー・バレンシア*3の打席の結果です.

これをJupyter(Python)とpandas, matplotlibを使って作ります.

f:id:shinyorke:20161211183324p:plain

なお, 主審(捕手)目線から見たプロットになるので要注意です!!!(≒パワプロと同じです)*4

念のため凡例を貼っておきます.

f:id:shinyorke:20161211184834p:plain

コード

ちょっとPythonを触れる方向けにサンプルコード(データ付属)を用意しました.

真似したい方はどうぞ&マー君とかマエケンとかココに無いデータでやりたい方は別途pitchpxでデータを取得してやってみてください(すぐ後の章で触れます).

サンプル(データ付き)

github.com

pitchpx(データセットの取得)

github.com

今回使ったモノ

プログラミング環境

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

qiita.com

この先はサンプルコードとデータが手元にある前提でお話を進めます.

【おさらい】野球のストライクゾーンについて

これであとはJupyter notebookを実行すれば出来上がり...なのですが,その前に野球のストライクゾーンの仕様についておさらいます.

なぜおさらいするかというと,

(趣味のものであれ)データ分析にはドメイン知識が必要だからです!

思わぬ落とし穴にはまらないよう,チェックしましょう.

とはいえ説明はこの絵一枚でおしまいです.

【図】ストライクゾーンの仕様

f:id:shinyorke:20161211194039p:plain

一言で言うと,

  • ストライクゾーンの幅は固定
  • ストライクゾーンの高さは可変,打者の体格によって変わる

です!

ちなみに今回は用いませんが,奥行きは固定(これもホームベースと同じ)です.

普段野球を見たりやったりしている人には常識ですが,あまりルールに慣れてない方は再確認しましょう.

Pitch f/xデータの座標系仕様(ざっくり)

ストライクゾーンの仕様が判明した所で,今回のデータの仕様を確認します.

例で上げたダルビッシュVSバレンシアの投球データの情報(データフレーム)はこんな感じです.

※必要なデータのみ抜粋しています

f:id:shinyorke:20161211194709p:plain

それぞれのカラムの意味は,本家アメリカに加え日本のブログでも紹介されています.(素晴らしい!!!)*5

tech.atware.co.jp

pitchfx glossary | Fast Balls

具体的には,

  • 本塁の場所を基準点とする
  • 横軸(x)は本塁からの距離(px)を使う
  • 縦軸(y)は本塁からの高さ(pz)を
  • 残りのデータはラベル情報として使う

ということになります.

なお,ストライクゾーンは以下の用に定義しました.

  • 横幅(固定)は基準点から0.8(-0.8)フィートの位置に定義.
    • 厳密に言えば0.7(-0.7)ちょいですが, ホームベースの入り口から出口の流れを想定して敢えてマージンを取っています.
    • 本家で似たような事をしているサイト(BrooksBaseball.net: Home of the PITCHf/x Tool)も同じ感じだったので真似した
  • 高さ(可変)はsz_top(上限), sz_bot(下限)の平均値を使用.
    • センサーデータなので実は投げる度に変わる
    • とはいえ変わるの考慮するのはちょっとアレなので打席内の投球の平均で解決することに

座標データ

f:id:shinyorke:20161211200048p:plain

ラベル情報(実況データ)

サンプルコードからの抜粋&コメント付きで

# 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を元にうまくやってあげれば出てきます(雑)

Yu Darvish pithced(2016/8/17)

ポイントを少しだけ解説します.

データの読み込み

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あたりと組み合わせてカッコイイ一球速報を作れそう!
    • 実はサンプルの中に色々入ってるのだが,これは時間不足で断念した残骸w(いずれやる)
    • このサンプルっぽいのを最終的には作りたい!*6
  • この辺もライブラリ化して可視化を楽にしたくなってきた
  • 実験しやすいJupyterはやっぱ便利
  • 【反省点】キャッチャー視点での打席勝負がみられない
    • 優秀なキャッチャーとそうでないキャッチャーでストライクゾーンの違いが(野球用語で言う所のフレーミング
    • pitchpx(とpitchRx)では「誰が球を受けていたか」の情報は取れない
    • pitchpxの方は年末年始に対応する予定です!(Ver.3として)

次の方

どりらんさん,よろしくお願いします!

【Appendix】今回,参考にした書籍

Pythonと野球方面で.

Python

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

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

野球

Analyzing Baseball Data with R (Chapman & Hall/CRC The R Series)

Analyzing Baseball Data with R (Chapman & Hall/CRC The R Series)

*1:プロなり何なりが公開してくれないと無理

*2:なんやかんやで日本選手の例がやりやすい

*3:余談ですが脳震盪やら何やらあって来季からマリナーズでプレー

*4:Yahoo!などの一球速報は投手目線となります,投手目線で作りたい人はx軸を逆転させれば作れますがあまりオススメしません(世界標準は多分主審目線)

*5:pitchpxを使ってくれて本当に嬉しい

*6:どりらんさん本当にありがとうございます!!!