jupyter notebookで野球データをビジュアライズ part 1

クライマックスシリーズ突入!!

こんにちは!
ルーターエンジニアのohkabeです。
今回はクライマックスシリーズ突入という事で、野球のデータをjupyter notebookでビジュアライズします。
なんでも、野球の世界では統計学やデータ分析が盛んで、セイバーメトリクスと呼ばれる分析手法が、戦略や選手の獲得に大きく関わっているそうです。
日本シリーズに向けて、これからどんどん盛り上がっていく野球シーンをネタに、jupyter notebookはこんなことができるぞ!というのを紹介します。

下準備

初めに以下のモジュールを使用します。
matplotlibで日本語フォントを使用するために、otf変換したメイリオフォントをインストールしています。

import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
sns.set(style="ticks")

import matplotlib as mpl
font = {"family":"meiryo"}
mpl.rc('font', **font)

なお、今回使用するデータはプロ野球データFreakさんのものを使用しました。

pandasのread_htmlメソッドにより、Webページ内の表をデータフレームとして読み込むことができます。

url = 'https://baseball-data.com/team/hitter.html'
datasets = pd.io.html.read_html(url)
datasets[0]
順位 チーム 試合 勝利 敗北 NOI IsoD IsoP 得点平均 安打平均
0 順位 チーム 試合 勝利 敗北 NOI IsoD IsoP 得点平均 安打平均
1 1 広島 143 82 59 .493 .086 .169 5.04 8.92
2 2 ヤクルト 143 75 66 .481 .081 .136 4.60 9.00
3 3 巨人 143 67 71 .460 .068 .146 4.37 8.78
4 4 DeNA 143 67 74 .446 .057 .165 4.00 8.45
5 5 中日 143 63 78 .451 .059 .115 4.18 9.01
6 6 阪神 143 62 79 .450 .077 .109 4.03 8.54

以下では、必要に応じて加工したcsvを使用するものとします。

個人成績の散布図を作る

使用するデータは、2018年度セリーグの選手の打撃成績一覧です 。

batter_2018 = pd.read_csv("data_sets/batter_2018.csv")
batter_2018.head()
チーム 打率 打席数 安打 本塁打 打点 四球 死球 三振 犠打
0 中日 0.274 645 161 7 57 47 4 80 1
1 中日 0.235 632 136 4 44 19 6 111 26
2 広島 0.262 675 150 10 60 75 17 118 6
3 広島 0.233 642 130 13 60 51 3 111 30
4 DeNA 0.318 590 175 28 71 38 1 45 0

jointplotで2変数の関係を見る

seabornのjointplot関数で散布図を作ります。
x軸,y軸を、打率と本塁打として、その関係を見て見ましょう。
イメージでは、パワー系の選手は、テクニックが低くなりそうなので打率と本塁打は反比例すると予想します。

ax = sns.jointplot(batter_2018["打率"], batter_2018["本塁打"], batter_2018)

意外にも打率が3割付近の巧打の打者でなければ、ホームランを量産できないという結果が出ました。
聞いた話では、パワーでホームランを打つタイプの打者は最近では少ないそうです。

次は打率と犠打の関係を見ます。
自分の予想では、打率はそこそこですが守備が上手い、いぶし銀の選手がバントを得意としているイメージです。

ax = sns.jointplot(batter_2018["打率"], batter_2018["犠打"], batter_2018)

こちらはイメージ通りです。

swarmplot/violinplot

seabornでよく使うのはswarmplotとviolinplotで、swarmplotは散布図、violinplotは確率密度を出してくれます。
データフレームをチーム別に分解して描画して見ます。

plt.figure(figsize=(15, 8))
ax = sns.violinplot(x=batter_2018["チーム"], y =batter_2018["打率"], inner=None, color="0.95", linewidth=0.3)
ax = sns.swarmplot(x=batter_2018["チーム"], y =batter_2018["打率"])

横に並べることでチーム別の比較がしやすくなりました。
微差ではありますが、

  • 広島の打者は全体的に打率が高く、選手層が厚い
  • DeNAは分布の厚みが下の方にあるため、打撃が苦手

などの傾向が見えてきます。

チーム打撃のグラフ化

数値の標準化

では次にチーム全体の打撃成績を見て見ましょう。
使用するのは過去5年間の各チームの打率などを集めたデータです。

team_batting = pd.read_csv("data_sets/team_batting.csv")
team_batting.head(6)
年度 チーム 打率 得点 本塁打 盗塁 犠打
0 2018 ヤクルト 0.266 658 135 68 109
1 2018 中日 0.265 596 97 60 86
2 2018 広島 0.262 721 175 95 109
3 2018 巨人 0.257 625 152 61 96
4 2018 阪神 0.253 574 85 75 108
5 2018 DeNA 0.250 572 181 71 71

うーん、よく分かりません。
個人では打率3割が目安ですが、チーム打率が2割6分と言われてもピンときません。
ましてや、盗塁や犠打については全く分からない。
よって、ここはデータを標準化します。

def normalize(df):
df_norm = (df - df.mean()) / df.std()
return df_norm
team_batting[["打率","得点","本塁打","盗塁","犠打"]] = normalize(team_batting[["打率","得点","本塁打","盗塁","犠打"]])
team_batting.head(6)
年度 チーム 打率 得点 本塁打 盗塁 犠打
0 2018 ヤクルト 1.062737 1.148216 0.560872 -0.282448 0.012451
1 2018 中日 0.965238 0.306402 -0.757466 -0.698321 -1.214815
2 2018 広島 0.672742 2.003608 1.948595 1.121126 0.012451
3 2018 巨人 0.185248 0.700154 1.150654 -0.646337 -0.681221
4 2018 阪神 -0.204747 0.007694 -1.173783 0.081442 -0.040909
5 2018 DeNA -0.497244 -0.019461 2.156754 -0.126495 -2.015205

0に近いほど平凡な成績になるように標準化しました。
正だと良い、負だと悪いと思ってください。
例えば、2018年の阪神の75盗塁は、標準化すると0.08なので平均的よりちょっと良いです。

データフレームの正規化

ではグラフ描画のため、このデータフレームを正規化します。
正規化とはID一個に値が一個のテーブル構造にする事です。
「横持ちを縦持ちにする」とか「ピボットテーブルにする」と同じ意味です。
pandasではpd.melt()で正規化ができます。

batting_melt = pd.melt(team_batting, id_vars=["年度","チーム"], value_vars=["打率","得点","本塁打","盗塁","犠打"])
batting_melt.head(10)
年度 チーム variable value
0 2018 ヤクルト 打率 1.062737
1 2018 中日 打率 0.965238
2 2018 広島 打率 0.672742
3 2018 巨人 打率 0.185248
4 2018 阪神 打率 -0.204747
5 2018 DeNA 打率 -0.497244
6 2017 広島 打率 1.745228
7 2017 DeNA 打率 -0.302246
8 2017 巨人 打率 -0.594743
9 2017 阪神 打率 -0.594743

catplot

seabornのcatplotでグラフ化します。

ax = sns.catplot(x='年度', y='value', data=batting_melt, col='チーム', row='variable', kind='point')

グラフがたくさんでてきました。
行方向は打率、得点などの成績名、列方向はチーム名です。
中の折れ線グラフは年度ごとの成績の推移を表しています。
例えば一番左の列を見ればヤクルトの成績の年度推移が一目で見れるわけですが、
2017年度はどの項目も谷になっているので、この年は調子悪かったんだなと読み取れます。
DeNAの打率が低いところで安定しているのも目立ちますね。

おまけ – matplotlibで日本語フォント

matplotlibでは日本語が表示できないません。(全部□になってしまう)
matplotlibはttfとotf形式にしか対応しておらず、日本語フォントはほぼttcという形式だからです。
よって使える形式の日本語フォントを探すか、フォントの形式を変換してインストールすると解決します。
そして以下のコマンドを実行します。

import matplotlib as mpl
font = {"family":"meiryo"}
mpl.rc('font', **font)

# ダメな場合はmatplotlibのキャッシュをリセットする
mpl.font_manager._rebuild()

終わりに

part1はここまで。
part2は日本シリーズ開幕時に公開します。

弊社ではデータスクレイピングだけでなく、pythonを用いた機械学習やデータビジュアライズなども承っております。
最近流行りのデータサイエンスでソリューションを生み出しましょう。

データコンサルティングのご案内