Lean Baseball

No Engineering, No Baseball.

ダルビッシュ有さんが2017年に投げた投球データをPythonとBigQueryで軽く調べてみた

f:id:shinyorke:20180131173429p:plain

今日で週休七日生活が終わる野球エンジニアこと@shinyorkeです.

昨年末に,BigQueryに突っ込んだ野球データでダルビッシュ有さん(@faridyu)の投球データについてかる~く調べてみました.

「今年こそPythonでデータ分析するぞ!」

「BigQueryをPythonから使いたいぞ!」

っていう野球好きの方の参考になれば幸いです.

なお今回はホントにデータを覗き見した程度の軽いネタです.

TL;DR

  • 投球コースを散布図で可視化するといい感じになる
  • 球種と結果をSankey Diagramにするのも面白い
  • BigQueryとJupyter,pandasの組み合わせすっごい楽
  • 次回はPySparkあたりで学習とかさせたい
  • 多分おそらく@faridyuさんはここに書いた分析と傾向の斜め上をいくと思ういや行って欲しい(ファンとして)

Starting Member

BigQueryにMLB一球速報データを格納する(おさらい)

大晦日ハッカソン 2017で機構を作りました&昨年最後のエントリーにUPしてます,そちらをご参照ください.

shinyorke.hatenablog.com

必要なデータセットは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)

いい感じにできました.

f:id:shinyorke:20180131175407p:plain

グラフの読み方

何の説明もなく散布図で表現しましたが,ひと言でいうと「実況パワフルプロ野球と同じ」です.*1

この辺のスクショを参考にこの先の散布図を見ていただくと良いかと思います.

f:id:shinyorke:20161211184834p:plain

f:id:shinyorke:20161211194039p:plain

対右打者

まずは右打者編.

三振を奪った球の球種とコース

散布図

f:id:shinyorke:20180131175723p:plain

傾向

内角の真ん中〜高めのFF(フォーシーム・ファストボール,いわゆる直球)もしくは,外角低めの打者から逃げるSL(スライダー)がよく使われているっぽいです.

ダルビッシュさんはMLBでも屈指の本格派なのでこのようなストロングスタイルでも勝負できるのかなと.

内角低めではカーブ(CU),あとはほんの少しだけカットボール(FC)やツーシーム(FT)も使ってるっぽいです.

ホームランを打たれた球種とコース

散布図

f:id:shinyorke:20180131180304p:plain

傾向

スタイル的に多投している?高めのフォーシームが打たれている印象です.

球速とか回転数など,他の変数で説明が付きそうですがここではちょっと割愛.

対左打者

同じ調子で左打者を.

三振を奪った球の球種とコース

散布図

f:id:shinyorke:20180131180631p:plain

傾向

右打者と異なり,スライダー(SL)の割合が増えてます.

左打者の外角コース,px(横軸)の-0.4〜-1.0あたりのスライダーはおそらくバックドア(ストライクゾーンの外からゾーン内に入れるボール)と推測されます.

とはいえ全体的に外角高めのフォーシーム(FF)と,打者から見た時に真ん中から内角寄り(打者寄り)に襲ってくるスライダーで稼いでる印象があります.

ホームランを打たれた球種とコース

散布図

f:id:shinyorke:20180131181316p:plain

傾向

三振をキメるボールでスライダー増えてる理由がわかりました.

左打者相手だと,どのコースでもフォーシームが打たれています!

よくメジャー(&最近の日本)ではツーシームやカットボール等で「ボールを動かして」長打を防ぐスタイルが流行ってるのですが、ダルビッシュさんの場合は動かすボールの比率が少なそうです.

ちょいと理由まで把握してませんが...

球種毎の結果をSankey Diagramで可視化する

ということで,三振とホームランの傾向がわかったので,データの源泉と結果,流量を表す「Sankey Diagram」という方法で可視化してみます.

Python(Jupyter)上でSankey Diagramを使う

ipythonのウィジェットで可能です.

今回のエントリーではこちらを参考にさせてもらいました.

qiita.com

ライブラリはこちら(上記のQiitaで紹介されているもの).

github.com

今回は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)

これで,球種と結果ごとに投じられたボールが集計できます.

f:id:shinyorke:20180131183349p:plain

「え、ホームラン(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)

こんな感じで描けちゃいます,うんいい感じですね!

f:id:shinyorke:20180131183910p:plain

で,結果は...

左右の投球でやったらこうなりました.

f:id:shinyorke:20180131173429p:plain

絵としては綺麗ですが,何を示しているか若干微妙...

とはいえちょっと見えたのは,

「左打者相手のスライダー,案外ボールやヒット多い」

左打者相手のスライダーが三振時のウイニングショット...と先ほど結果で書きましたが,案外打たれたりボールになったりしてるのもありますねと.

これってチームが変わる前後(昨年前半はレンジャーズ,後半にドジャースに移籍)で変わってる可能性が高い...いや変わってるだろうと思うのですが,この辺の違いから掘ると面白い傾向が見えそうです.

次にやりたいこと→PySparkでクラスタリングなり機械学習をやる

というわけで,ここまでは機械学習とかいうギャンブルを使わなくてもできました.

この分析の続きですが,

  • ボールの球速・回転数
  • コース
  • 打席毎に投じたボールの順番(Pitch Sequence),いわゆる「配球」

を元に,PySparkで捏ねくり回して何かやろうかなと思います.

まあ機械学習も使えるかな...アイデアある方是非レスください!

なお,PySparkはこの辺を参考にインストールとか環境設定ができたので休みの人とかにやりたいと思います!

入門 PySpark ―PythonとJupyterで活用するSpark 2エコシステム

入門 PySpark ―PythonとJupyterで活用するSpark 2エコシステム

speakerdeck.com

明日からいよいよ野球の仕事,楽しみです!

【Appendix】参考文献とサンプルコード

この辺の分析をやりたい人が読むべきエントリーと本

マネー・ボールの理解程度じゃちょっと足りないので,この辺の本を読んで理解できると良いでしょう.

ビッグデータ・ベースボール 20年連続負け越し球団ピッツバーグ・パイレーツを甦らせた数学の魔法

ビッグデータ・ベースボール 20年連続負け越し球団ピッツバーグ・パイレーツを甦らせた数学の魔法

プロ野球を統計学と客観分析で考える デルタ・ベースボール・リポート1

プロ野球を統計学と客観分析で考える デルタ・ベースボール・リポート1

  • 作者: 岡田友輔,市川博久,大南淳,水島仁,蛭川皓平,Student,神事努,神原謙悟,竹下弘道,高多薪吾
  • 出版社/メーカー: 水曜社
  • 発売日: 2017/09/27
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る

また,技術書関連はこちら.

PythonユーザのためのJupyter[実践]入門

PythonユーザのためのJupyter[実践]入門

Pythonではじめるデータラングリング ―データの入手、準備、分析、プレゼンテーション

Pythonではじめるデータラングリング ―データの入手、準備、分析、プレゼンテーション

  • 作者: Jacqueline Kazil,Katharine Jarmul,嶋田健志,長尾高弘
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2017/04/26
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る

また,過去に自分でやったこれらのネタが非常に参考になりました,1年以上前のワイよくやった.

shinyorke.hatenablog.com

サンプルコード

Kawasaki.rb #56 でLTしたネタから発展させました.

ダルビッシュ有の2017年投球データをかるーく可視化する

*1:データの表現・可視化という観点でパワプロ方式ホント素晴らしい,本筋と関係ないですが.