2019年度のセパ両リーグのピッチャーの成績データを偏相関係数でみる
2019年度のセパ両リーグのピッチャーの成績データを偏相関係数でみる
2019年度のセパ両リーグのピッチャーの成績データの変数間の偏相関係数がどうなっているか見てみる. 特に勝利数と防御率に着目する.
repository:
データ
日本野球機構の選手成績データを使用する.
今回は,防御率の低い選手のセ・リーグ9人,パ・リーグ6人とし, これではデータが少ないので,さらに,セパともに12人になるように追加したデータ計24人を使用する. 残り人数は"登板が特に少なく防御率が低くなってしまっているような選手は除いて""独断で選択した.
データのカラムは今回計24にした. しかしカテゴリデータ含み,多重共線性も考慮するので全ては使っていない(後述).
- 名前
- 所属
- 身長[cm]
- 体重[kg]
- 出生日
- 登板
- 勝利
- 敗北
- セーブ
- H
- HP
- 完投
- 完封勝
- 無四球
- 勝率
- 打者
- 投球回
- 被安打
- 被本塁打
- 四球
- 死球
- 三振
- 暴投
- ボーク
- 失点
- 自責点
- 防御率
- URL
偏相関係数行列をみる
データの読みこみ
まず,pandasでcsvを読み込む.
import scipy as sp from scipy import linalg import numpy.linalg as linalg_np import pandas as pd def cor2pcor(R): inv_cor = linalg.inv(R, check_finite=True) rows = inv_cor.shape[0] regu_1 = 1 / sp.diag(inv_cor) regu_2 = sp.repeat(regu_1, rows).reshape(rows, rows) pcor = (-inv_cor) * sp.sqrt(regu_1 * regu_2) sp.fill_diagonal(pcor, 1) return pcor
df = pd.read_csv("2019.csv", header=0, encoding="utf-8") df
> 選手名 所属 身長[cm] 体重[kg] 出生日 登板 勝利 敗北 セーブ H ... 被本塁打 四球 死球 三振 暴投 ボーク 失点 自責点 防御率 URL 0 大野 雄大 中日 183 83 1988-09-26 25 9 8 0 0 ... 18 43 2 156 2 0 52 51 2.58 http://npb.jp/bis/players/11515133.html ....
名前,所属,出生日,URLは使わないので除去する. また,すべての0の変数も使えないので除去する.
# zero only のカラム除外 df_nonzero = df.loc[:, (df != 0).any(axis=0)] # 名前,所属,出生日,URLの除外 # 自責点と失点はほぼ一致するので多重共線性のために除外 df_out = df_nonzero.drop(["選手名","所属","出生日","URL"], axis=1) df_out
> 身長[cm] 体重[kg] 登板 勝利 敗北 セーブ H HP 完投 完封勝 ... 被安打 被本塁打 四球 死球 三振 暴投 ボーク 失点 自責点 防御率 0 183 83 25 9 8 0 0 0 2 2 ... 132 18 43 2 156 2 0 52 51 2.58
pairplot
全て量データになったので散布図を見てみる.
今回はseabornのpairplot
を使う.
ただ,変数が多い(24)のでかなり重い.
df_out.shape > (24, 24)
import pandas as pd import matplotlib.pyplot as plt import seaborn as sns sns.set(font='Yu Gothic') %matplotlib inline sns.pairplot(df_out) plt.savefig("pairplot.png") plt.show()
変数は上から順に
特にH,HPは0,1に集中してしまっているので後で除去を考える.
相関係数行列
R = df_out.corr() R
せっかくなので行列をヒートマップで描画する.
import pandas as pd import matplotlib.pyplot as plt import seaborn as sns sns.set(font='Yu Gothic') %matplotlib inline fig = plt.figure(figsize=(9.5,9.5)) column_name = R.columns.to_list() # column_name sns.heatmap(R.values, annot=True, square=True, vmin=-1, vmax=1, fmt=".2f", cmap="jet", xticklabels=column_name, yticklabels=column_name) plt.savefig("corr_allcol.png") plt.title("cor") plt.show()
多重共線性(multicollinearity)の検討
共分散行列(相関係数行列)のrankを調べると特定は難しいがある程度わかる.
R.shape, linalg_np.matrix_rank(R) > ((24, 24), 23)
ランク=23よりランク落ちしていることがわかる.
偏相関係数での主な原因:
標本の大きさより変数の数のほうが多い.(http://seetheworld1992.hatenablog.com/entry/2017/03/22/194932) 共分散行列は正則にならない=正定値でない
また,対角成分の符号がそろっていないと多重共線性の疑い (偏相関係数の分母のルートの計算ができない)
今回は,以下のようにした.
除外したデータからもう一度相関係数行列を計算する.
df_out2 = df_out.drop(["勝率","H","HP","自責点"], axis=1) df_out2 > 身長[cm] 体重[kg] 登板 勝利 敗北 セーブ 完投 完封勝 無四球 打者 投球回 被安打 被本塁打 四球 死球 三振 暴投 ボーク 失点 防御率 0 183 83 25 9 8 0 2 2 0 696 177.2 132 18 43 2 156 2 0 52 2.58
ランクをチェックする. 今度は一致したので問題が無さそうに見える.
R_out = df_out2.corr() R_out.shape,linalg_np.matrix_rank(R_out) > ((20, 20), 20)
さらに,この相関係数行列の逆行列の対角成分の符号が一致しているかをチェックする.
diag = sp.diag(linalg.inv(R_out.values)) def check_same_sign(a): return sp.all(a<=0) or sp.all(a>=0) check_same_sign(diag), diag > (True, array([1.40502100e+01, 1.39937845e+01, 4.39575580e+01, 3.46214472e+01, 1.05610560e+02, 8.24634204e+01, 1.14923312e+02, 4.99263398e+01, 4.57451039e+01, 8.87932854e+03, 4.97257190e+03, 8.56826106e+02, 6.51735948e+01, 1.46269658e+02, 8.59730521e+00, 4.59628134e+01, 4.91618538e+00, 3.97753488e+00, 1.92351483e+02, 9.91691391e+00]))
問題無さそうなので相関係数行列のヒートマップをプロットする.
import pandas as pd import matplotlib.pyplot as plt import seaborn as sns sns.set(font='Yu Gothic') %matplotlib inline fig = plt.figure(figsize=(9,9)) column_name = df_out2.columns.to_list() # column_name R_out = df_out2.corr() sns.heatmap(R_out.values, annot=True, square=True, vmin=-1, vmax=1, fmt=".2f", cmap="jet", xticklabels=column_name, yticklabels=column_name) plt.savefig("corr_decDim.png") plt.title("cor") plt.show()
偏相関係数行列
pcor = cor2pcor(R_out.values) pcor
ヒートマップでプロットする.
import matplotlib.pyplot as plt import seaborn as sns sns.set(font='Yu Gothic') %matplotlib inline fig = plt.figure(figsize=(9.5,9.5)) sns.heatmap(pcor, annot=True, square=True, vmin=-1, vmax=1, fmt=".2f", cmap="jet", xticklabels=column_name, yticklabels=column_name) plt.savefig("pcor.png") plt.title("pcor") plt.show()
条件付き独立を調べる
この20項目与えられたときの条件付き独立をみる. 偏相関係数が(-0.1,0.1)の範囲を条件付き独立としてみる.
ind_cond = (-0.1 < pcor) & (pcor < 0.1) c = sp.where(ind_cond) col = R_out.columns list_cond_indepentset = set() for i, j in zip(c[0], c[1]): s = tuple(sorted((col[i], col[j]))) list_cond_indepentset.add(s) list_cond_indepentset
{('セーブ', '体重[kg]'), ('セーブ', '身長[cm]'), ('ボーク', '勝利'), ('ボーク', '打者'), ('ボーク', '投球回'), ('ボーク', '暴投'), ('三振', '打者'), ('三振', '投球回'), ('三振', '登板'), ('勝利', '登板'), ('四球', '登板'), ('打者', '登板'), ('投球回', '登板'), ('死球', '防御率'), ('登板', '防御率')}
ボーク回数や登板回数が他の変数と独立関係にあることが多いとわかる.
選手の優秀さを表すような勝利数や防御率に着目すると,
- 勝利数とボーク数は条件付き独立独立(無関係)
- 勝利数が多いと登板数も多いように直感的に思うが,勝利数と登板数は条件付き独立
- 防御率と死球は条件付き独立
- 防御率が低い(優秀である)と登板数も増えそうだが,防御率と登板数は条件付き独立
条件付き独立の要素のみプロットしてみる.
%matplotlib inline fig = plt.figure(figsize=(9,9)) sns.heatmap(pcor, annot=True, mask=sp.logical_not(ind_cond), square=True, vmin=-1, vmax=1, fmt=".2f", cmap="jet", xticklabels=column_name, yticklabels=column_name) plt.savefig("pcorr_only_mask_cond.png") plt.title("pcor only cond.ind.") plt.show()
勝利数に着目
勝利数がどの変数と偏相関があるかを見る.
idx = column_name.index("勝利") pcor_winnum = pd.Series(pcor[idx], index=column_name) pcor_winnum.sort_values(ascending=False)
勝利 1.000000 四球 0.642020 無四球 0.603793 被本塁打 0.586582 投球回 0.579033 完封勝 0.575050 被安打 0.574459 暴投 0.536554 死球 0.497758 セーブ 0.377383 防御率 0.352614 体重[kg] 0.311432 ボーク 0.081238 登板 -0.059880 身長[cm] -0.239034 三振 -0.340608 打者 -0.506458 失点 -0.513885 完投 -0.589127 敗北 -0.724989 dtype: float64
- 当たり前のところで行くと,勝利数が多いとその分敗北数が少ない(-0.72), そして失点も少ない(-0.514),完封勝利も多い(0.575).
- 勝利数が多いと,四球が多く(0.64)また無四球試合も多い(0.60). 直感に反するが,おそらく意図的にボール球を投げられるかのコントロール性の高さを表している?
- 勝利数が多い選手は,完投が少ない(-0.589)
- 勝利数が多い選手は,被本塁打も多い,HRはなんだかんだ打たれている(0.587).そして,被安打も多い,HRよりも低いがほぼ同等ヒットもなんだかんだ打たれている(0.574)
- 勝利数が多い選手は,投球回数も多い.直感に合う(0.579)
- 勝利数が多い選手は,暴投も多い(0.537),そして死球も多い(0.498)
- 勝利数が多い選手は,打者として打席に立つのは少ない(-0.506)
- 勝利数が多い選手は,セーブ数も多い(0.377)
- 勝利数が多い選手は,防御率も高い(0.353).防御率は低い方が高評価なのでこれは直感に反する.勝利数と敗北数が同じくらいの一部選手の偏りの可能性あり
- 勝利数が多い選手は,奪三振が少ない(-0.34).これも直感に反する.
- 勝利数が多い選手は,体重が重い傾向(0.31).逆に身長は低い傾向(-0.239)にある.ただこれはサンプルの偏りの可能性もあり
条件付き独立をマスクした偏相関係数行列をプロットしてみる.
%matplotlib inline fig = plt.figure(figsize=(9,9)) sns.heatmap(pcor, annot=True, mask=ind_cond, square=True, vmin=-1, vmax=1, fmt=".2f", cmap="jet", xticklabels=column_name, yticklabels=column_name) plt.savefig("pcorr_mask_cond.png") plt.title("pcor widthout cond.ind.") plt.show()
防御率について
同様に防御率でみる.
idx = column_name.index("防御率") pcor_winnum = pd.Series(pcor[idx], index=column_name) pcor_winnum.sort_values(ascending=False)
防御率 1.000000 失点 0.648739 敗北 0.546813 完投 0.527748 三振 0.386311 勝利 0.352614 打者 0.284757 身長[cm] 0.218110 登板 0.078456 死球 -0.002031 暴投 -0.200287 体重[kg] -0.352797 投球回 -0.359943 被安打 -0.412098 ボーク -0.413773 セーブ -0.441115 四球 -0.441360 被本塁打 -0.466979 完封勝 -0.511341 無四球 -0.530140 dtype: float64
- 防御率の低い(優秀な)選手は,失点が少ない(0.649).
- 防御率の低い選手は,敗北も少ない(0.547).
- 防御率の低い選手は,無四球試合が多い(-0.530),そして四球も多い.(-0.441).
- 防御率の低い選手は,完投が少ない(0.528).
- 防御率の低い選手は,完封勝ちは多い(-0.511)
- 防御率の低い選手は,被本塁打は多い.HRはそれなりに打たれている(-0.467), そして被安打も多い(-0.412)
- 防御率の低い選手は,セーブは多い.(-0.441)
- 防御率の低い選手は,ボークが多い(-0.413).ボークほどでないが暴投も多い傾向(-0.2).
- 防御率の低い選手は,奪三振が少ない(0.386).直感に反する.
- 防御率の低い選手は,投球回は多い(-0.36).
- 防御率の低い選手は,体重が重い傾向(-0.353),逆に身長は低い傾向(0.218).これは勝利数のときと同じだが,サンプルの偏りの可能性あり.
- 防御率の低い選手は,勝利数は少ない(直感に反する)(0.353)
- 防御率の低い選手は,打者として打席に立つのは少ない(直感に反する)(0.285)