はしくれエンジニアもどきのメモ

情報系技術・哲学・デザインなどの勉強メモ・備忘録です。

2019年度のセパ両リーグのピッチャーの成績データを偏相関係数でみる

2019年度のセパ両リーグのピッチャーの成績データを偏相関係数でみる

2019年度のセパ両リーグのピッチャーの成績データの変数間の偏相関係数がどうなっているか見てみる. 特に勝利数と防御率に着目する.

repository:

github.com

データ

日本野球機構の選手成績データを使用する.

npb.jp

今回は,防御率の低い選手のセ・リーグ9人,パ・リーグ6人とし, これではデータが少ないので,さらに,セパともに12人になるように追加したデータ計24人を使用する. 残り人数は"登板が特に少なく防御率が低くなってしまっているような選手は除いて""独断で選択した.

データのカラムは今回計24にした. しかしカテゴリデータ含み,多重共線性も考慮するので全ては使っていない(後述).

  • 名前
  • 所属
  • 身長[cm]
  • 体重[kg]
  • 出生日
  • 登板
  • 勝利
  • 敗北
  • セーブ
  • H
  • HP
  • 完投
  • 完封勝
  • 無四球
  • 勝率
  • 打者
  • 投球回
  • 被安打
  • 本塁打
  • 四球
  • 死球
  • 三振
  • 暴投
  • ボーク
  • 失点
  • 自責点
  • 防御率
  • URL

csvにまとめたものをgithubにおいてある.

相関係数行列をみる

データの読みこみ

まず,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()

変数は上から順に

  • 身長[cm]
  • 体重[kg]
  • 登板
  • 勝利
  • 敗北
  • セーブ
  • H
  • HP
  • 完投
  • 完封勝
  • 無四球
  • 勝率
  • 打者
  • 投球回
  • 被安打
  • 本塁打
  • 四球
  • 死球
  • 三振
  • 暴投
  • ボーク
  • 失点
  • 自責点
  • 防御率

pairplot

特に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よりランク落ちしていることがわかる.

相関係数での主な原因:

今回は,以下のようにした.

  • 自責点,失点,防御率は相関高い -> 防御率の計算に使っているため -> 自責点は除外

  • 勝率は勝敗と登板から計算できるので除外

  • H, HPはほぼ0なので除外

除外したデータからもう一度相関係数行列を計算する.

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)