Lean Baseball

No Engineering, No Baseball.

Pandasとmatplotlibを使って日本の強打者Hideki Matsuiさんの打球の行方をグラフ化してみた #pyhack

f:id:shinyorke:20150711201504p:plain

本日のPython mini hack-a-thon #55で作ったネタです。

pyhack.connpass.com

今日で #pyhack が55回目とのことで、55といえば我らが誇る強打者GodzillaことHideki Matsuiやろ!

って事でRETROSHEETから松井秀喜さんの打撃データを抽出し、グラフで書いてみました。

おしながき

  • 松井選手の打撃データ(RETROSHEET)
  • データを取得する
  • 前処理 - Pandasでデータを読み込み&ややこしい行データからヒットの種類と落ちた場所を抽出する
  • 可視化 - matplotlibでグラフを書いてみる
  • 分析 - 絶好調な松井さんと引退前の松井さんの特徴について

松井選手の打撃データ(RETROSHEET)

まずデータですが、いつもどおりRETROSHEET(http://www.retrosheet.org/)のCSVデータを使っています。

松井秀喜選手がメジャーで実働していたのは2003年から2012年までなので、この期間のデータをGetし、データベース化します。

データベース化についてはお決まりのこちらのエントリーで作ったネタを使います。

最強の野球オープンデータ「Retrosheet」をPython+Vagrant+Ansibleで誰でも使えるようにしました - Lean Baseball

既にサーバーは作成済みで、2010~2014年のデータがある状態なので、足りない2009~2003年のデータを足すplaybookを作って実行しました。

data_create.yml

ansible-playbook -i hosts data_create.yml

とかこんな感じでコマンドを叩いてしばらく待つとデータができていました*1

データを取得する

データができたら、次のSQLを叩いて結果をCSVに保存します。

Hideki Matsuiさんの2004年打撃データの中でヒットの情報(安打/二塁打/三塁打/本塁打)を取得する

select e.game_id, e.event_id, e.event_cd, e.pitch_seq_tx, e.event_tx, e.bat_play_tx, e.battedball_cd, e.battedball_loc_tx from games as g left outer join events as e on g.game_id = e.game_id where e.bat_id = 'matsh001' and g.game_dt between 20040101 and 20041231 and e.event_cd in(20, 21, 22, 23) order by g.game_dt asc, e.event_id asc

from句ではevents(打席データ)とgames(試合データ)を結合しています。

select句のカラムはあとで解説するとして、where句の意味は、

where e.bat_id = 'matsh001'

eventsテーブルのbat_id(バッターの選手ID)カラムの中身がHideki Matsui(id:matsh001、IDの振り方は謎)のやつを取って来い!という意味です。

g.game_dt between 20040101 and 20041231

こちらは試合の期間です。2004/1/1~2004/12/31まで取得という意味です。

厳密に言えば、オフシーズンとプレーオフの期間(秋~冬)の期間は外すべきですが、このデータにプレーオフの情報は無いため、この記述でもデータは取れます。

e.event_cd in(20, 21, 22, 23)

eventsデータにはevent_cdという、打席のイベント(ヒット、三振、四球など)が記録されており、この中でヒットのモノだけを取得します。

これでSQLを実行し、結果を「hideki_matsui_2004.csv」という名前(csv)で保存します。

同じ要領で2011年のデータも取得します。

select e.game_id, e.event_id, e.event_cd, e.pitch_seq_tx, e.event_tx, e.bat_play_tx, e.battedball_cd, e.battedball_loc_tx from games as g left outer join events as e on g.game_id = e.game_id where e.bat_id = 'matsh001' and g.game_dt between 20110101 and 20111231 and e.event_cd in(20, 21, 22, 23) order by g.game_dt asc, e.event_id asc

日付の絞込を2004から2011に書き換え、ファイル名も「hideki_matsui_2011.csv」とでもしておきます。

前処理 - Pandasでデータを読み込み&ややこしい行データからヒットの種類と落ちた場所を抽出する

取得したcsvPythonで料理!、、、する前に、一度ファイルの中身をチェックしてみましょう。

f:id:shinyorke:20150711205804p:plain

正直わけわからんですね!!!w

今回の分析で使う項目は、

  • event_cd
  • event_tx
  • battedball_cd

の3つで、これらの意味が何となくわかればOKです。

event_cd

先ほどのSQLでも登場したカラムです。

打席で発生したイベント。0~25くらいまでありますが、SQLを発行した時点で以下に絞られています。

event_tx

event textの略。このエントリーの肝となるデータです。

打席で起きた結果とランナーの動きを示したテキストで、イベントの結果、発生場所、塁上の動き(走塁)をデリミタ("/"区切り)で表現しています。

例えば、

S9/G.1-3

という表記は、

「ゴロ性の当たりのライト前ヒット、一塁ランナーは三塁まで進塁」

を意味します。読み方ですが、

  • 先頭は打球の種類。今回はヒットで絞っているため、S:Single(安打)、D:Double(二塁打)、T:Triple(三塁打)、HR:Homerun(本塁打)、DGR:Ground Rule Double(エンタイトルツーベース)のどれか。
  • 数字は打球が落ちた場所。1(投手)~9(右翼手)の守備番号で表記。
  • 打球の種類を表すアルファベット1文字。これは後述するbattedball_cdと同じ。

今回はこのテキストから打球の種類と落ちた場所を抽出します。

抽出するコードはこんな感じで書きました。

retrosheet_util.py

battedball_cd

打球の種類。

  • G:Ground ball。日本風に言えばゴロ気味の当たり。転がって内野の間を抜けたりするアレ。
  • F: Fly ball。上がった打球の事。外野の頭上を越える当たり(二塁打三塁打、ホームランなど)が該当。
  • L: Line drive。鋭い打球、ライナー性の打球のこと。単打もあれば、フェンスの低い球場の場合ライナー性のホームランが含まれる事も。

event_txからヒットの情報と場所ごとにカウントしてデータフレームを作る、というコードはこちら。

使い方はすぐこの後の章を参考にしてね。

analyze_batter.py

可視化 - matplotlibでグラフを書いてみる

これらのコードを書き上げた後、いつもどおりIPython notebookで実行してみました。

hideki_matsui.py

トドメとしてこんなコードを書いてみるとあら不思議、外野ポジションごとに飛んだ打球の数と種類が出てきます。

dfpos2004 = pd.DataFrame(hit_charts2004.get('of')) dfpos2004.T.plot(kind='bar', stacked=True, ylim=(0, 100), title='Hideki Matsui(2004/NYY)')

f:id:shinyorke:20150711212653p:plain

dfpos2011 = pd.DataFrame(hit_charts2011.get('of')) dfpos2011.T.plot(kind='bar', stacked=True, ylim=(0, 100), title='Hideki Matsui(2011/OAK)')

f:id:shinyorke:20150711212733p:plain

分析 - 絶好調な松井さんと引退前の松井さんの特徴について

グラフを見ての感想&仮説はこちら。

f:id:shinyorke:20150711212943p:plain

あとはやきうが詳しい方と松井さんのファンにお任せします!

*1:ただ、1時間近くかかってたのでこのプロジェクトのコードを描き直そうかと思いました。多分無駄なこといっぱいしてる。