Python3
baseball
野球

PythonでMLB選手の得点圏打率のヒストグラムを作成

More than 1 year has passed since last update.

概要

shinyorke(Shinichi Nakagawa)さんが作成したMLBデータ分析用ライブラリhattebergをベースに、自分なりに改良を加えてMLB選手の得点圏打率のヒストグラムを作成しました。

きっかけ

元々データ好き&野球好きということもあり、数年前から野球のデータを自分でいじってみたいなーと思っていました。そんな中、MLBの野球データを用いた分析と可視化のサンプル(Python)を公開しましたという記事を見つけました。記事を見つけた当時(2年前)はPythonを触ったことがなかったものの、野球データのためならとPythonの勉強を開始。自分の仕事の都合で進捗が悪かったが、少しだけ形になってきたので今回記事にしました。

MLBデータ分析用ライブラリhattebergとは

今回利用したMLBデータ分析用ライブラリhattebergは、

  • MLBの試合の詳細データRetrosheetのcsvファイルを元にデータベースを作成
  • データベースから選手の打撃(投球)結果等の抜き出し、解析、可視化

を行うPythonコードが詰まった夢のようなライブラリです。

ただ、ここでは特徴をざっくり言っただけなので、詳しい使い方等は、やはりオリジナルの記事githubに記載されている内容を是非参照頂ければと思います。

MLB選手の得点圏打率のヒストグラム作成

自分は野球のデータの中で、得点圏打率というものに興味を持っており、データ分析の初歩として得点圏打率に関わる分析を行いました。

オリジナルのhattebergのライブラリでは得点圏打率を出せるようにはなっていないので、得点圏時の選手の打撃結果を得るために、ライブラリのretrosheet_app内のコードに以下のような記述を追加しました(追加位置はコード内の適当な箇所)。

retrosheet_util.py
# 得点圏時のイベント
   RISP = {
        2: ('2B only',),
        3: ('1B & 2B',),
        4: ('3B only',),
        5: ('1B & 3B',),
        6: ('2B & 3B',),
        7: ('Loaded',),
    }
retrosheet_controller.py
    # 得点圏時のを表すMySQLクエリ
    QUERY_SELECT_BATTING_STATS_WHERE_RISP = "and e.start_bases_cd in({bases_codes})"
    # 得点圏時の選手の打撃結果等を抜き出すMySQLクエリを作成
    QUERY_SELECT_BATTING_STATS_RISP_AT_BAT = " ".join(
        [
            QUERY_SELECT_BATTING_STATS,
            QUERY_SELECT_BATTING_STATS_WHERE,
            QUERY_SELECT_BATTING_STATS_WHERE_AT_BAT,
            QUERY_SELECT_BATTING_STATS_WHERE_RISP,
            QUERY_SELECT_BATTING_STATS_ORDER_BY
        ]
    )

    # ある選手の得点圏時の打撃結果を取得する関数
    def batter_event_risp_at_bat(self, first_name, last_name, year, from_dt=DEFAULT_FROM_DT, to_dt=DEFAULT_TO_DT):

        bases_codes = (str(cd) for cd in RetroSheetUtil.RISP.keys())
        return self._batter_risp_query(self.QUERY_SELECT_BATTING_STATS_RISP_AT_BAT,
                                       player_id, year, from_dt, to_dt, bases_codes)

    # MySQLにクエリを投げる関数呼び出しのための関数
    def _batter_risp_query(self, query, first_name, last_name, year, from_dt, to_dt, bases_codes):
        batter = self.get_player_data_one(year, first_name, last_name)
        params = self._batter_event_query_params(batter, year, from_dt, to_dt)
        params['bases_codes'] = ",".join(bases_codes)

        return self.read_sql_query(query.format(**params))

最低限これを準備すれば、batter_event_risp_at_batのメソッドを呼び出すことで、DataFrame形式の得点圏時の選手の打撃結果が取得できるはずです。
(上記の例ではオリジナルに合わせて、選手のFirst NameとLast Nameで情報を得たい選手を指定しているが、まれに名前被りがあるようなので、実際の自分のコードでは選手固有のIDで取得するようにしています。多数の選手を無作為に取り出したい場合などは、選手固有のIDを用いるようにした方がよいかもしれません。)

このような改良を加えた上で、打撃結果を打率に変換する俺々解析用プログラムを組んで、MLBの選手の得点圏打率をヒストグラムにしてみました。以下が結果です。

Average_RISP_Histgram.png
Average_RISP_Sub_Histgram.png

上の図は通算得点圏打率に対する打者数のヒストグラムで、下の図は(通算得点圏打率-通算打率)に対する打者数のヒストグラムです。今回は、統計的にゆらぎが少なくなるように、2001年から2016年の間に2000打席以上の打者に限定し、その通算の得点圏打率と打率を用いています。仮に1996年から2008年の活動期間の選手でも、2001年から2008年の通算ということになります。

上の図も下の図も比較的きれいに正規分布になっているような感じです。
上の図を見てみると、得点圏打率が突出して高い(0.36ぐらい)選手がいますが、これはバリー・ボンズですね。彼は異常です。

下の図を出した意図としては、「チャンスに強い打者などいない」という話もちらほら聞くので、仮にそうだとしたら、いかなる打者でも多くの打席を重ねる上で、通算得点圏打率-通算打率は0に近づいていくと思ったからです。ところがそうはなっておりません。
中には、得点圏の打席数が1500を超えていても、通算得点圏打率が通算打率より0.03以上あるような選手もいた(逆も然り)ので、個人的には、チャンスに強い・弱いは存在するのではないかと思っています。ただし、それは精神的にどうこうというよりは、得点圏という特別な状況に強い(弱い)打撃の特徴を持っている選手が存在するのではないかと考えてます。

まとめ

今回は、MLBデータ分析用ライブラリhattebergを自分なりに改良して、MLB選手の得点圏打率のヒストグラムを作成しました。実際は、もう少し突っ込んだ分析もしておりますが、野球界隈の勉強会?もあるようなので、うまく分析が進めば(進まなくても)、その辺を狙って発表できないかなーと思っておりますので、今回は突っ込んだ内容は出しておりません。そのうち出せたらと思いますので、ご了承下さい。

最後に、野球データに興味があるけど、一歩踏み出せないという方は、もしかしたらこのライブラリを使用するところから始めてみてもよいかもしれません!

謝辞

hattebergがなければ、自分ひとりでここまでたどり着くのはかなり難しかったと思います。shinyorke(Shinichi Nakagawa)さん、ライブラリを公開していただき本当にありがとうございます。

参考リンク