今日で週休七日生活が終わる野球エンジニアこと@shinyorkeです.
昨年末に,BigQueryに突っ込んだ野球データでダルビッシュ有さん(@faridyu)の投球データについてかる~く調べてみました.
「今年こそPythonでデータ分析するぞ!」
「BigQueryをPythonから使いたいぞ!」
っていう野球好きの方の参考になれば幸いです.
なお今回はホントにデータを覗き見した程度の軽いネタです.
TL;DR
- 投球コースを散布図で可視化するといい感じになる
- 球種と結果をSankey Diagramにするのも面白い
- BigQueryとJupyter,pandasの組み合わせすっごい楽
- 次回はPySparkあたりで学習とかさせたい
- 多分おそらく@faridyuさんはここに書いた分析と傾向の斜め上をいくと思ういや行って欲しい(ファンとして)
Starting Member
- TL;DR
- Starting Member
- BigQueryにMLB一球速報データを格納する(おさらい)
- ダルビッシュ有さんの投球データをBigQueryから取得する
- 打者の左右毎で三振を奪ったボールとホームランを打たれたボールの傾向を眺める
- 球種毎の結果をSankey Diagramで可視化する
- 次にやりたいこと→PySparkでクラスタリングなり機械学習をやる
- 【Appendix】参考文献とサンプルコード
BigQueryにMLB一球速報データを格納する(おさらい)
大晦日ハッカソン 2017で機構を作りました&昨年最後のエントリーにUPしてます,そちらをご参照ください.
必要なデータセットはMLB AMが,ライブラリや手法は私が公開しているモノの範疇で可能です...ので,頑張れば真似できると思います.
この先の解説は,このエントリーで上げた環境ができていることを前提に解説します.
具体的には,Jupyter上でpandasとBigQuery,bokehが使えればOKです.
ダルビッシュ有さんの投球データをBigQueryから取得する
Pitch(投球)テーブルからクエリ一発でとれます.
Pythonのコードで書くとこんな感じです.
# ダルビッシュさんが投じた全投球 query = "select * from [mlbam.pitch] where pit_box_name = 'Darvish'" df_darvish = pd.read_gbq(query, 'example-yakiu-db')
この後は左右のP毎にsummaryしてDataFrameを作ります.
# 打者の打席(右 or 左)ごとにDataframeを分ける(ちなみにダルさんは右投げ) df_darvish_vs_right = df_darvish.query("bat_hand_cd == 'R'") # 右 df_darvish_vs_left = df_darvish.query("bat_hand_cd == 'L'") # 左
これで可視化・分析ができる状態が整いました,ガンガン行きましょう.
打者の左右毎で三振を奪ったボールとホームランを打たれたボールの傾向を眺める
対戦打者の左右毎に,三振を奪ったウイニングショットとホームランを打たれて「飛翔」したボールのコースと球種を見てみましょう.
ひとまず散布図を描く
bokehで散布図をエイっと描きます.
対右打者の投球データをプロットします.
# まずは試運転がてら,ひとまず散布図に出す from bokeh.charts import Scatter, output_notebook, show from bokeh.models import Range1d output_notebook() # 縦軸:pz(ホームベースからの高さ), 横軸:px(ホームベースからの距離) p = Scatter(df_darvish_vs_right[['px', 'pz']], plot_width=600, plot_height=600) p.x_range=Range1d(-3, 3) p.y_range=Range1d(-1, 6) show(p)
いい感じにできました.
グラフの読み方
何の説明もなく散布図で表現しましたが,ひと言でいうと「実況パワフルプロ野球と同じ」です.*1
この辺のスクショを参考にこの先の散布図を見ていただくと良いかと思います.
対右打者
まずは右打者編.
三振を奪った球の球種とコース
散布図
傾向
内角の真ん中〜高めのFF(フォーシーム・ファストボール,いわゆる直球)もしくは,外角低めの打者から逃げるSL(スライダー)がよく使われているっぽいです.
ダルビッシュさんはMLBでも屈指の本格派なのでこのようなストロングスタイルでも勝負できるのかなと.
内角低めではカーブ(CU),あとはほんの少しだけカットボール(FC)やツーシーム(FT)も使ってるっぽいです.
ホームランを打たれた球種とコース
散布図
傾向
スタイル的に多投している?高めのフォーシームが打たれている印象です.
球速とか回転数など,他の変数で説明が付きそうですがここではちょっと割愛.
対左打者
同じ調子で左打者を.
三振を奪った球の球種とコース
散布図
傾向
右打者と異なり,スライダー(SL)の割合が増えてます.
左打者の外角コース,px(横軸)の-0.4〜-1.0あたりのスライダーはおそらくバックドア(ストライクゾーンの外からゾーン内に入れるボール)と推測されます.
とはいえ全体的に外角高めのフォーシーム(FF)と,打者から見た時に真ん中から内角寄り(打者寄り)に襲ってくるスライダーで稼いでる印象があります.
ホームランを打たれた球種とコース
散布図
傾向
三振をキメるボールでスライダー増えてる理由がわかりました.
左打者相手だと,どのコースでもフォーシームが打たれています!
よくメジャー(&最近の日本)ではツーシームやカットボール等で「ボールを動かして」長打を防ぐスタイルが流行ってるのですが、ダルビッシュさんの場合は動かすボールの比率が少なそうです.
ちょいと理由まで把握してませんが...
球種毎の結果をSankey Diagramで可視化する
ということで,三振とホームランの傾向がわかったので,データの源泉と結果,流量を表す「Sankey Diagram」という方法で可視化してみます.
Python(Jupyter)上でSankey Diagramを使う
ipythonのウィジェットで可能です.
今回のエントリーではこちらを参考にさせてもらいました.
ライブラリはこちら(上記のQiitaで紹介されているもの).
今回はJupyter notebook上に関数を書いて,DataFrameを捏ねくり回して作りました.
球種・イベント(安打・ボール・アウトetc...)毎に集計
pandasのpivot tableで集計しました.
pitch_idがuniqueに振られているっぽかったのでこちらで数え上げ.
# まずはデータセットをサマリーする. import numpy as np def pitch_summary(pitch_df): return pitch_df.pivot_table( index='pitch_type', # 球種 columns='pa_event_cd', # ホームラン:23,アウト:2,etc...イベントごとのコード values=['pitch_id'], aggfunc=np.count_nonzero ).fillna(0) df_summary_right = pitch_summary(df_darvish_vs_right)
これで,球種と結果ごとに投じられたボールが集計できます.
「え、ホームラン(23)実際より多くね???」と見えますが,これは,「ホームランを打たれた打席で投じられたボール」で数えているためこうなります.
今回はひとまず傾向をみたいだけなのでこのまま行きます.
Sankey Diagramを描く
集計したDataFrameからSankey用のデータセット(Dictionary)に変換
# pa_event_code = target, pitch_type = source TARGET_PARAMS = { 2: 'out', 3: 'strikeout', 14: 'ball', 15: 'intent', 16: 'hbp', 18: 'error', 19: 'fc', 20: '1b', 21: '2b', 22: '3b', 23: 'hr', } TYPE_PARAMS = { 2: 'generic out', 3: 'strike out', 14: 'ball', 15: 'other', 16: 'other', 18: 'other', 19: 'other', 20: 'hit', 21: 'slugging', 22: 'slugging', 23: 'run', } def summary2links_list(df): _links = [] for k, v in df.to_dict().items(): for source, pitch_count in v.items(): if int(pitch_count) <= 0: continue _links.append( { 'source': source, 'target':TARGET_PARAMS.get(k[1]), 'value': int(pitch_count), 'type': TYPE_PARAMS.get(k[1]), } ) return _links links_right = summary2links_list(df_summary_right)
描画の関数はこんな感じ
from ipysankeywidget import SankeyWidget from ipywidgets import Layout layout = Layout(width="900", height="600") def sankey(margin_top=10, **value): """Show SankeyWidget with default values for size and margins""" return SankeyWidget(layout=layout, margins=dict(top=margin_top, bottom=0, left=30, right=60), **value) sankey(links=links_right)
こんな感じで描けちゃいます,うんいい感じですね!
で,結果は...
左右の投球でやったらこうなりました.
絵としては綺麗ですが,何を示しているか若干微妙...
とはいえちょっと見えたのは,
「左打者相手のスライダー,案外ボールやヒット多い」
左打者相手のスライダーが三振時のウイニングショット...と先ほど結果で書きましたが,案外打たれたりボールになったりしてるのもありますねと.
これってチームが変わる前後(昨年前半はレンジャーズ,後半にドジャースに移籍)で変わってる可能性が高い...いや変わってるだろうと思うのですが,この辺の違いから掘ると面白い傾向が見えそうです.
次にやりたいこと→PySparkでクラスタリングなり機械学習をやる
というわけで,ここまでは機械学習とかいうギャンブルを使わなくてもできました.
この分析の続きですが,
- ボールの球速・回転数
- コース
- 打席毎に投じたボールの順番(Pitch Sequence),いわゆる「配球」
を元に,PySparkで捏ねくり回して何かやろうかなと思います.
まあ機械学習も使えるかな...アイデアある方是非レスください!
なお,PySparkはこの辺を参考にインストールとか環境設定ができたので休みの人とかにやりたいと思います!
入門 PySpark ―PythonとJupyterで活用するSpark 2エコシステム
- 作者: Tomasz Drabas,Denny Lee,Sky株式会社玉川竜司
- 出版社/メーカー: オライリージャパン
- 発売日: 2017/11/22
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (3件) を見る
明日からいよいよ野球の仕事,楽しみです!
【Appendix】参考文献とサンプルコード
この辺の分析をやりたい人が読むべきエントリーと本
マネー・ボールの理解程度じゃちょっと足りないので,この辺の本を読んで理解できると良いでしょう.
ビッグデータ・ベースボール 20年連続負け越し球団ピッツバーグ・パイレーツを甦らせた数学の魔法
- 作者: トラヴィス・ソーチック,桑田健,生島淳
- 出版社/メーカー: KADOKAWA/角川書店
- 発売日: 2016/03/16
- メディア: 単行本
- この商品を含むブログ (3件) を見る
プロ野球を統計学と客観分析で考える デルタ・ベースボール・リポート1
- 作者: 岡田友輔,市川博久,大南淳,水島仁,蛭川皓平,Student,神事努,神原謙悟,竹下弘道,高多薪吾
- 出版社/メーカー: 水曜社
- 発売日: 2017/09/27
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
また,技術書関連はこちら.
- 作者: 池内孝啓,片柳薫子,岩尾エマはるか,@driller
- 出版社/メーカー: 技術評論社
- 発売日: 2017/09/09
- メディア: 大型本
- この商品を含むブログ (1件) を見る
Pythonではじめるデータラングリング ―データの入手、準備、分析、プレゼンテーション
- 作者: Jacqueline Kazil,Katharine Jarmul,嶋田健志,長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2017/04/26
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
また,過去に自分でやったこれらのネタが非常に参考になりました,1年以上前のワイよくやった.
サンプルコード
Kawasaki.rb #56 でLTしたネタから発展させました.
*1:データの表現・可視化という観点でパワプロ方式ホント素晴らしい,本筋と関係ないですが.