はじめに
こんにちは!
今回はPythonを使う練習も兼ねて、何か実際のデータを分析したいと考えました。そこで、僕が大好きな野球選手の成績を使ってデータ分析をしてみたいと思います。
この記事は【初心者向け】33分4秒でおぼえる野球統計学とPyData〜陽岱鋼を添えての記事の流れを大いに参考にさせていただきました。
セイバーメトリクスについて
昨今、野球もデータ分析が盛んになってきており、セイバーメトリクスという分析手法が浸透しております。
Wikipediaによるとセイバーメトリクスは
セイバーメトリクス (SABRmetrics, Sabermetrics) とは、野球においてデータを統計学的見地から客観的に分析し、選手の評価や戦略を考える分析手法である。
とあります。
セイバーメトリクスの考え方でいくと、打者に関しては「アウトにならないこと」や「一つでも次の塁に進むこと」が重要視され、今まで野球で有効とされていた送りバントや盗塁のようなアウトになる確率が高い攻撃はあまり選ばれない傾向にあるようです。
内川聖一選手の年度別成績からセイバーメトリクス指標を算出
さて本題です。
今回は、僕が愛してやまない福岡ソフトバンクホークスの内川聖一選手の成績を使ってセイバーメトリクス指標を算出します。
ご存知の方もいらっしゃるかもしれませんが、内川選手は稀代のヒットメーカーです。過去には右打者として歴代最高打率の.378を叩き出したことでも有名です。しかし、今年は開幕前の練習試合での成績がよろしくなく、今シーズンは二軍生活を余儀なくされています。ただ、二軍では高打率をキープしており、近いうちに一軍に上がってくることを心から祈っています!
とりあえずそこで、改めて内川選手の特徴を年度別成績から浮き彫りにし、一軍に合流した際の起用方法に関して考えたいと思います。
もちろんのこと、ここでの考察は筆者の個人的見解になるので、あまり深く突っ込まないでくださいね…
必要な準備
とりあえず今回必要なPythonライブラリをインストールします。
僕はJupyter notebook上で全て行ったので、行頭に!
をつけて操作しました。
!pip install ipython pandas beautifulsoup4 numpy lxml html5lib jupyter matplotlib seaborn
そうしたら、グラフをnotebook内に描画するために%matplotlib inline
と、pandasのインポート、表示行列を増やしました。
%matplotlib inline
import pandas as pd
# 表示行・列を増やす(30列、10行)
pd.get_option("display.max_columns", 30)
pd.get_option("display.max_rows", 10)
データ収集
以上の準備ができたら、内川選手のデータを入手しましょう。
データはNPB公式サイトの内川選手のページからスクレイピングします。(スクレイピングは用法用量に注意しましょう!)
url = 'https://npb.jp/bis/players/21325113.html' #内川聖一選手の個人年度別成績(npb.jp)
df = pd.read_html(url) #Tableタグをスクレイピング。
df
には複数のTableが含まれます。df[0]
には選手のプロフィールが、df[1]
には年度別の成績が含まれています。
df[0] #選手のプロフィール
0 | 1 | |
---|---|---|
0 | ポジション | 内野手 |
1 | 投打 | 右投右打 |
2 | 身長/体重 | 185cm/93kg |
3 | 生年月日 | 1982年8月4日 |
4 | 経歴 | 大分工 |
5 | ドラフト | 2000年ドラフト1位 |
df[1] #年度別の成績
年度 | 所属球団 | 試合 | 打席 | 打数 | 得点 | 安打 | 二塁打 | 三塁打 | 本塁打 | ... | 盗塁刺 | 犠打 | 犠飛 | 四球 | 死球 | 三振 | 併殺打 | 打率 | 長打率 | 出塁率 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2001.0 | 横 浜 | 3 | 2 | 2 | 1 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.000 | 0.000 | 0.000 |
1 | 2002.0 | 横 浜 | 42 | 73 | 66 | 11 | 22 | 4 | 1 | 2 | ... | 0 | 3 | 0 | 4 | 0 | 13 | 1 | 0.333 | 0.515 | 0.371 |
2 | 2003.0 | 横 浜 | 45 | 161 | 150 | 20 | 47 | 5 | 0 | 4 | ... | 4 | 6 | 0 | 5 | 0 | 13 | 1 | 0.313 | 0.427 | 0.335 |
3 | 2004.0 | 横 浜 | 94 | 369 | 338 | 55 | 97 | 11 | 1 | 17 | ... | 7 | 12 | 1 | 18 | 0 | 42 | 7 | 0.287 | 0.476 | 0.322 |
4 | 2005.0 | 横 浜 | 90 | 262 | 234 | 33 | 64 | 11 | 0 | 5 | ... | 1 | 6 | 1 | 19 | 2 | 36 | 10 | 0.274 | 0.385 | 0.332 |
5 | 2006.0 | 横 浜 | 124 | 439 | 402 | 41 | 115 | 15 | 2 | 4 | ... | 3 | 7 | 3 | 22 | 5 | 64 | 9 | 0.286 | 0.363 | 0.329 |
6 | 2007.0 | 横 浜 | 92 | 274 | 247 | 24 | 69 | 17 | 3 | 7 | ... | 4 | 4 | 1 | 16 | 6 | 37 | 9 | 0.279 | 0.457 | 0.337 |
7 | 2008.0 | 横 浜 | 135 | 544 | 500 | 83 | 189 | 37 | 1 | 14 | ... | 3 | 5 | 4 | 31 | 4 | 49 | 7 | 0.378 | 0.540 | 0.416 |
8 | 2009.0 | 横 浜 | 132 | 552 | 503 | 65 | 160 | 32 | 2 | 17 | ... | 5 | 2 | 4 | 42 | 1 | 56 | 16 | 0.318 | 0.491 | 0.369 |
9 | 2010.0 | 横 浜 | 144 | 637 | 577 | 75 | 182 | 36 | 4 | 9 | ... | 2 | 3 | 4 | 47 | 6 | 51 | 17 | 0.315 | 0.438 | 0.371 |
10 | 2011.0 | 福岡ソフトバンク | 114 | 463 | 429 | 48 | 145 | 21 | 3 | 12 | ... | 0 | 0 | 7 | 25 | 2 | 48 | 3 | 0.338 | 0.485 | 0.371 |
11 | 2012.0 | 福岡ソフトバンク | 138 | 567 | 523 | 44 | 157 | 21 | 3 | 7 | ... | 4 | 0 | 7 | 31 | 6 | 36 | 12 | 0.300 | 0.392 | 0.342 |
12 | 2013.0 | 福岡ソフトバンク | 144 | 633 | 570 | 76 | 180 | 33 | 1 | 19 | ... | 0 | 0 | 5 | 46 | 12 | 47 | 23 | 0.316 | 0.477 | 0.376 |
13 | 2014.0 | 福岡ソフトバンク | 122 | 534 | 488 | 50 | 150 | 26 | 1 | 18 | ... | 0 | 0 | 7 | 34 | 5 | 48 | 5 | 0.307 | 0.475 | 0.354 |
14 | 2015.0 | 福岡ソフトバンク | 136 | 585 | 529 | 60 | 150 | 24 | 1 | 11 | ... | 0 | 0 | 7 | 45 | 4 | 55 | 24 | 0.284 | 0.395 | 0.340 |
15 | 2016.0 | 福岡ソフトバンク | 141 | 605 | 556 | 62 | 169 | 19 | 0 | 18 | ... | 2 | 0 | 9 | 38 | 2 | 53 | 27 | 0.304 | 0.435 | 0.345 |
16 | 2017.0 | 福岡ソフトバンク | 73 | 300 | 266 | 31 | 79 | 13 | 0 | 12 | ... | 1 | 0 | 2 | 32 | 0 | 26 | 9 | 0.297 | 0.481 | 0.370 |
17 | 2018.0 | 福岡ソフトバンク | 71 | 296 | 281 | 27 | 68 | 11 | 0 | 8 | ... | 1 | 0 | 3 | 9 | 3 | 32 | 6 | 0.242 | 0.367 | 0.270 |
18 | 2019.0 | 福岡ソフトバンク | 137 | 535 | 500 | 49 | 128 | 21 | 0 | 12 | ... | 0 | 1 | 4 | 28 | 2 | 49 | 16 | 0.256 | 0.370 | 0.296 |
19 | NaN | 通 算 | 1977 | 7831 | 7161 | 855 | 2171 | 357 | 23 | 196 | ... | 37 | 49 | 69 | 492 | 60 | 755 | 202 | 0.303 | 0.442 | 0.350 |
20 rows × 23 columns
前処理
データが取得できたので、前処理をしていきます。
まずは、一軍での試合数が少ない2003年以前と通算の成績を除外して、新しいデータフレームを作成します。
#不使用となるデータを捨てて、別のデータフレーム作成(一軍での試合数が少ない2003年以前と通算)
atbats = df[1].drop([0, 1, 2, 19], axis=0)
そしたら、カラム名をおしゃれに野球英語にします。
日本語名 | 英語名 |
---|---|
年度 | year |
所属球団 | team |
試合 | g |
打席 | pa |
打数 | ab |
得点 | r |
安打 | h |
二塁打 | _2b |
三塁打 | _3b |
本塁打 | hr |
塁打 | tb |
打点 | rbi |
盗塁 | sb |
盗塁刺 | cs |
犠打 | sh |
犠飛 | sf |
四球 | bb |
死球 | hbp |
三振 | so |
併殺打 | dp |
打率 | ba |
長打率 | slg |
出塁率 | obp |
ついでにデータ型もfloat
型に変換します。
# カラム名を付与する(野球英語の略称)
atbats.columns = ['year', 'team', 'g', 'pa', 'ab', 'r', 'h', '_2b', '_3b', 'hr', 'tb', 'rbi', 'sb', 'cs', 'sh', 'sf', 'bb', 'hbp', 'so', 'dp', 'ba', 'slg', 'obp']
# 各カラムを前処理する
import numpy as np
atbats['year'] = atbats['year'].astype(np.float64)
atbats['g'] = atbats['g'].astype(np.float64)
atbats['pa'] = atbats['pa'].astype(np.float64)
atbats['ab'] = atbats['ab'].astype(np.float64)
atbats['r'] = atbats['r'].astype(np.float64)
atbats['h'] = atbats['h'].astype(np.float64)
atbats['_2b'] = atbats['_2b'].astype(np.float64)
atbats['_3b'] = atbats['_3b'].astype(np.float64)
atbats['hr'] = atbats['hr'].astype(np.float64)
atbats['tb'] = atbats['tb'].astype(np.float64)
atbats['rbi'] = atbats['rbi'].astype(np.float64)
atbats['sb'] = atbats['sb'].astype(np.float64)
atbats['cs'] = atbats['cs'].astype(np.float64)
atbats['sh'] = atbats['tb'].astype(np.float64)
atbats['sf'] = atbats['sf'].astype(np.float64)
atbats['bb'] = atbats['bb'].astype(np.float64)
atbats['hbp'] = atbats['hbp'].astype(np.float64)
atbats['so'] = atbats['so'].astype(np.float64)
atbats['dp'] = atbats['dp'].astype(np.float64)
atbats['ba'] = atbats['ba'].astype(np.float64)
atbats['slg'] = atbats['slg'].astype(np.float64)
atbats['obp'] = atbats['obp'].astype(np.float64)
セイバーメトリクス指標を可視化
データフレームができたので、セイバーメトリクス指標を可視化していきます。
ひとまず可視化にseabornを使用します。
# 可視化としてseabornを使用
import seaborn as sns
まずは、OPSとBB/Kを算出します。
OPS
OPSは打席あたりで得点増加に有効な打撃をしているかどうかを表す指標です。つまり打席での貢献度を示します。
平均は.730前後で、.1000を越えればリーグ最高レベルとのこと。
〈計算方法〉
OPS = 長打率 + 出塁率
BB/K
四球と三振の割合から打者の選球眼を見る指標です。1前後が望ましく、理想は1.5〜2とのこと。
〈計算方法〉
BB/K = 四球 / 三振
これらを計算していきます。
# OPSとBB/Kを計算
atbats['ops'] = atbats['obp'] + atbats['slg'] # OPS
atbats['bb_k'] = atbats['bb'] / atbats['so'] # BB/K
OPSの結果
atbats['ops']
3 0.798
4 0.717
5 0.692
6 0.794
7 0.956
8 0.860
9 0.809
10 0.856
11 0.734
12 0.853
13 0.829
14 0.735
15 0.780
16 0.851
17 0.637
18 0.666
Name: ops, dtype: float64
# OPSを折れ線グラフに
sns.pointplot(x="year", y="ops", data=atbats)
BB/Kの結果
atbats['bb_k']
3 0.428571
4 0.527778
5 0.343750
6 0.432432
7 0.632653
8 0.750000
9 0.921569
10 0.520833
11 0.861111
12 0.978723
13 0.708333
14 0.818182
15 0.716981
16 1.230769
17 0.281250
18 0.571429
Name: bb_k, dtype: float64
# BB/Kを折れ線グラフに
sns.pointplot(x="year", y="bb_k", data=atbats)
以上の結果からわかること
- OPSは平均以上のことが多いが、打率の割にリーグトップレベルまでは行かない。
- BB/Kの結果からは、四球が三振より多いとはあまり言えない。ただ、三振数は他の選手と比べて数は少ないので、極端に四球が少ないとも考えられる。
次はwOBAを見てみましょう。
wOBA
打者が打席あたりにどれだけチームの得点増に貢献する打撃をしているかを評価する指標です。安打や四球など出塁を伴う要素に得点価値を加重して算出されます。
出塁の価値を全て均一とみなす出塁率よりも打撃の貢献を総合的に表し、加重が統計的な根拠に基づいていることからOPSよりも適切に打撃の価値を評価すると言われています。
出塁率に合うように設計されているため、平均的な打者で.330程度になるようです。
〈計算式〉
wOBA = (0.7×(四球+死球)+0.9×単打+1.3×二塁打+1.6×三塁打+2.0×本塁打)÷(打数+四球+死球+犠飛)
(係数はシーズンによって変わるようですが、今回は数年の結果を見るのでベーシックな係数を使用しました。)
# wOBA
atbats['woba'] = ( 0.7 * ( atbats['bb'] + atbats['hbp'] ) + 0.9 * ( atbats['h'] ) + 1.3 * ( atbats['_2b']) + 1.6 * ( atbats['_3b']) + 2.0 * atbats['hr'] ) / ( atbats['ab'] + atbats['bb'] + atbats['hbp'] + atbats['sf'])
atbats['woba']
3 0.419608
4 0.377344
5 0.354398
6 0.438519
7 0.505195
8 0.459818
9 0.429180
10 0.443844
11 0.376190
12 0.450395
13 0.437640
14 0.383077
15 0.398017
16 0.448000
17 0.337500
18 0.351124
Name: woba, dtype: float64
# wOBAを折れ線グラフに
sns.pointplot(x="year", y="woba", data=atbats)
wOBAの結果からわかること
- wOBAは、調子の悪かったここ2年を含めて平均以上。また、.400を超えることも多く、かなり高水準であることがわかる(2020年9月7日時点で柳田選手は.460、オリックスの吉田正尚選手は.451、日本ハムの中田翔選手は.401)。
では次はIsoPを見てみましょう。
IsoP
打者が長打(二塁打以上)を放つ能力を測る指標です。長打率から単打の要素を除くことでより純粋な長打力を測ることができます。安打が全てシングルヒットの場合は0になります。平均は.130程度になるようです。
〈計算式〉
IsoP = 長打率-打率
# IsoP
atbats['isop'] = atbats['slg'] - atbats['ba']
atbats['isop']
3 0.189
4 0.111
5 0.077
6 0.178
7 0.162
8 0.173
9 0.123
10 0.147
11 0.092
12 0.161
13 0.168
14 0.111
15 0.131
16 0.184
17 0.125
18 0.114
Name: isop, dtype: float64
# IsoPを折れ線グラフに
sns.pointplot(x="year", y="isop", data=atbats)
IsoPの結果からわかること
- IsoPは、.200を越えることなく推移。長打がそこまで多くない選手といえる(2020年9月7日時点で中田翔選手は.340、柳田選手は.328)。
最後にIsoDを算出します。
IsoD
四死球によってどの程度出塁したかを測るための指標です。平均は.060程度になるようです。
〈計算式〉
IsoD = 出塁率-打率
# IsoD
atbats['isod'] = atbats['obp'] - atbats['ba']
atbats['isod']
3 0.035
4 0.058
5 0.043
6 0.058
7 0.038
8 0.051
9 0.056
10 0.033
11 0.042
12 0.060
13 0.047
14 0.056
15 0.041
16 0.073
17 0.028
18 0.040
Name: isod, dtype: float64
# IsoDを折れ線グラフに
sns.pointplot(x="year", y="isod", data=atbats)
IsoDの結果からわかること
- IsoDは、平均の.060を下回ることがほとんどであり、四死球で出塁することは多いとはいえない。
考察
以上の結果から、内川選手の特徴として以下のことが挙げられると考えられます。
- 四死球が極端に少ないが、wOBAが高く、安打での出塁の確率がかなり高い選手
- ただし、ホームランなどの長打を打つというよりは単打や二塁打などが多い
- つまり肩書きに負けない生粋の安打製造機
- ただ、近年は調子は落ち気味?しかし、現在二軍では絶好調!
これを踏まえて、現時点で仮に一軍に合流した際のチームの打順を考えます。
- 周東(二)
- 柳田(中)
- 内川(一)
- グラシアル(三 or DH)
- 中村晃(左)
- 栗原(右)
- デスパイネ(DH) or 松田(三)
- 甲斐(捕)
- 川瀬(遊)
うーん、難しい…異論は認めます!笑
ただ、内川選手をどこに置くか考えると3番あたりがベストな気がします。
以上、Pythonを使って内川選手の年度別打撃成績をセイバーメトリクスで分析し、その特徴をとらえてみました。
セイバーメトリクスを使うことで、改めて違った見方で選手のことを見れそうです。勉強になりましたー!
コメント