はじめに
セリーグの2023年レギュラーシーズンが終わり、優勝は阪神タイガース。2位は広島東洋カープ、3位は横浜DeNAベイスターズとなりました。
今年のセリーグは岡田監督が復帰初年度で阪神を久しぶりの優勝に導き、カープの新監督は、シーズン前に評論家の順位予想がBクラスでしたが、それに負けず「家族」をテーマに2位、DeNAは新外国人のバウアー(YouTuber)選手が2桁勝利を挙げ、侍ジャパンの牧選手が打点王を獲得するなどして2年連続Aクラス入りしました。
ここで今季の順位表をスポナビさんから拝借させていただきます
https://baseball.yahoo.co.jp/npb/standings/detail/1 から引用
順位 | チーム名 | 勝利 | 敗戦 | 引分 | 勝率 | 勝差 | 得点 | 失点 | 本塁打 | 盗塁 | 打率 | 防御率 | 失策 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 阪神 | 85 | 53 | 5 | .616 | 優勝 | 555 | 424 | 84 | 79 | .247 | 2.66 | 85 |
2 | 広島 | 74 | 65 | 4 | .532 | 11.5 | 493 | 508 | 96 | 78 | .246 | 3.20 | 82 |
3 | DeNA | 74 | 66 | 3 | .529 | 0.5 | 520 | 496 | 105 | 33 | .247 | 3.16 | 69 |
4 | 巨人 | 71 | 70 | 2 | .504 | 3.5 | 523 | 507 | 164 | 48 | .252 | 3.39 | 54 |
5 | ヤクルト | 57 | 83 | 3 | .407 | 13.5 | 534 | 567 | 123 | 62 | .239 | 3.66 | 70 |
6 | 中日 | 56 | 82 | 5 | .406 | 0 | 390 | 498 | 71 | 36 | .234 | 3.08 | 79 |
この順位表を基にPythonでデータ分析をしていきます
最後にオマケあり
分析するテーマ
- 得点と失点の関係
- 本塁打と得点の関係
- 盗塁と得点の関係
- 打率と得点の関係
- 防御率と勝利数の関係
- 防御率と失策の関係
環境構築
pip install pandas numpy seaborn matplotlib japanize-matplotlib
コード
必要なライブラリを設定します
順位表はCSV形式で保存したものを読み込んでDataFrame型にします
import pandas as pd
import numpy as np
import seaborn as sns
from matplotlib import pyplot as plt
import japanize_matplotlib
# セリーグの順位表を読み込む
df = pd.read_csv('baseball/data/order.csv')
# 凡例のチームカラーの設定
team_colors = {
'阪神': 'yellow',
'広島': 'red',
'DeNA': 'blue',
'巨人': 'orange',
'ヤクルト': 'navy',
'中日': 'cyan'
}
得点と失点のグラフ
plt.figure(figsize=(10, 5))
sns.scatterplot(data=df, x='得点', y='失点', hue='チーム名', palette='tab10', s=100)
plt.title('得点と失点の関係')
plt.xlabel('得点')
plt.ylabel('失点')
plt.grid(True)
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()
plt.show()
縦軸に得点数、横軸に失点数のグラフです。
得点と失点の平均線をそれぞれ青、赤色で示しています。
各ポイントの左上の数字は順位を表しています。
阪神は得点数555、失点数424で、1試合あたりに約4得点、3失点あり、1試合あたり得失点が+1と優勝にふさわしい結果です。
各球団の得失点差は阪神が+131、広島が-15、DeNAが+24、巨人が+16、ヤクルトが-33、中日が-108です。
ヤクルトは村神様が昨年の成績だと134打点、今年が84打点で50打点少ないので、去年ほど打っていればヤクルトの得失点差はプラスです(カロリーゼロ理論のよう)
中日は得失点差がマイナス108で得点力不足が顕著ですが、2軍打撃コーチの中村紀さんがスタメンを張り2001年の132打点の輝きを取り戻せばAクラスは狙えていました。
広島は得失点差がマイナスで2位に位置しており、新井監督の采配の上手さが伺えます。
グラフから読み取るにヤクルトが顕著な例ですが、得点数が多いチームは失点数が多い傾向にあるように見えます(得点が534、失点が567)。
阪神は比較的に得点が多く、失点が少なく、得点と失点の平均線の左上に位置しており、攻守で強さを見せていることが分かります。
3位のDeNAも平均線で区切られた左上に位置しているため、采配などの他の要因では2位を狙えたのではないかと考えられます。
平均線の右上への分布は平均より得点数が多く、失点数が多いという打線は強いが、投手陣を中心とした守りに弱さが見られます。ここに4位、5位の巨人とヤクルトが位置しています。来年に向けて失点数の改善が課題です。
平均線の右下への分布は平均より得点が少なく、失点が多いという打線が弱く、守りも弱いということが言えますが、そんな中で2位に位置している新井カープはすごいですね(ファンの心の中「点取れませーん。点取られまくりでーす。だけど2位。CS1stはMAZDA!!」)
平均線の左下への分布は平均より得点が少なく、失点が少ないという打線が弱く、守りが強いということが言えます。中日は2022年オフに複数の野手を他球団に放出した影響からか得点数が著しく低い結果となりました。
サッカーのJ1のデータを見ると、たいていの場合は順位が高いほど得点が高く、失点が少ない傾向になりますが
今季のセリーグがあまり顕著に見られませんでした。
本塁打と得点の関係
plt.figure(figsize=(10, 5))
sns.scatterplot(data=df, x='本塁打', y='得点', hue='チーム名', palette=team_colors, s=100)
plt.title('本塁打と得点の関係')
plt.xlabel('本塁打')
plt.ylabel('得点')
plt.grid(True)
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()
for i, row in df.iterrows():
plt.text(
row['本塁打'], row['得点'], str(row['順位']), color='black',
fontsize=10, ha='right', va='bottom'
)
# 得点と失点の平均値を示す線を追加
plt.axvline(df['本塁打'].mean(), color='red', linestyle='--', label='本塁打の平均')
plt.axhline(df['得点'].mean(), color='blue', linestyle='--', label='得点の平均')
# 相関係数
correlation_coefficient = np.corrcoef(df['本塁打'], df['得点'])[0, 1]
plt.text(0.1, 0.9, f'r: {correlation_coefficient:.2f}', transform=plt.gca().transAxes)
# 回帰直線
slope, intercept = np.polyfit(df['本塁打'], df['得点'], 1)
x = np.linspace(min(df['本塁打']), max(df['本塁打']), 100)
y = slope * x + intercept
plt.plot(x, y, color='black', label=f'回帰直線: y = {slope:.2f}x + {intercept:.2f}', alpha=0.2)
plt.show()
優勝した阪神は平均値と比べて本塁打が少ないが得点数が多いです。
得点をホームランに頼らず、ヒットと四球で得点機会を増やして攻撃しているのではないでしょうか。
阪神と同じ傾向のDeNAも本塁打がリーグ平均より少しだけ少ないですが、ホームランに頼らない攻撃をしていると言えるでしょう。
平均線で区切られた右上への分布は、平均より本塁打が多く、得点が多く、長打もあり攻撃力のある相手チームに威圧感を与えるような打線でしょうか。4位の巨人と5位のヤクルトがここに位置しています。
平均線の左下への分布は、平均より本塁打が少なく、得点数が少ない、いわゆる貧打と言われます。2位の広島と6位の中日がここに位置しています。
平均線の右下への分布は、平均より本塁打が多く、得点数が少なく、得点の多くを本塁打に頼っている傾向にありますが、この傾向にあるチームは今季では見られませんでした。
また、1本の本塁打が平均して何点の得点に寄与しているかを計算することができます。これにより、本塁打の価値やチームの得点能力を評価することができます。
チーム名 | 得点 | 本塁打 | 本塁打の価値 |
---|---|---|---|
阪神 | 555 | 84 | 6.61 |
広島 | 493 | 96 | 5.14 |
DeNA | 520 | 105 | 4.95 |
巨人 | 523 | 164 | 3.19 |
ヤクルト | 534 | 123 | 4.34 |
中日 | 390 | 71 | 5.49 |
※ 本塁打の価値 = 得点 / 本塁打
この結果から、阪神の本塁打の価値が最も高く、巨人の本塁打の価値が最も低いことがわかります。
阪神が本塁打を放つことで得られる得点が多いですが、巨人は多くの本塁打を放っても得点に結びつきにくい可能性があります。
たまたま、本塁打の価値が1番高いのが優勝したチーム、1番低いのがBクラスのチームとなっていますが、5.49の中日がBクラスに位置しているため本塁打の価値が高いからといって勝つチームと言いきれません。
得点が多い/少ない、本塁打の価値が高い/低いで、チームの戦術や打線の特性や課題が分かってくると思います。
盗塁と得点の関係
plt.figure(figsize=(10, 5))
sns.scatterplot(data=df, x='盗塁', y='得点', hue='チーム名', palette=team_colors, s=100)
plt.title('盗塁と得点の関係')
plt.xlabel('盗塁')
plt.ylabel('得点')
plt.grid(True)
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()
for i, row in df.iterrows():
plt.text(
row['盗塁'], row['得点'], str(row['順位']), color='black',
fontsize=10, ha='right', va='bottom'
)
# 得点と失点の平均値を示す線を追加
plt.axvline(df['盗塁'].mean(), color='red', linestyle='--', label='盗塁の平均')
plt.axhline(df['得点'].mean(), color='blue', linestyle='--', label='得点の平均')
plt.show()
縦軸に得点数、横軸に盗塁数のグラフです。
相関係数は0.48で正の相関が見られます
優勝した阪神は平均より盗塁が多く、得点が多く、盗塁を絡めて得点を重ねていることが分かります。
同じ傾向のヤクルトも盗塁が多く、得点を重ねています。ヤクルトの攻撃に盗塁が影響を与えている可能性があります。
平均線で区切られた左上への分布は盗塁が少なく、得点が多く、攻撃を盗塁に頼らずホームランなどに頼っていると言えるでしょう。3位のDeNAと4位の巨人が位置しています。各チームの戦術によりますが、盗塁技術を磨き、出塁で相手投手へプレッシャーを与えるのを増やす必要があるかもしれません。
平均線の右上への分布は盗塁が多く、得点が少なく、盗塁が得点に結びついていない傾向にあります。2位の広島は得点力向上に向けて盗塁以外で改善する必要がありそうです。
ただ、さすがカープ。伝統的に盗塁が多く足攻めのチームです。
平均線の左下への分布は盗塁が少なく、得点が少ない中日は戦術や打線を改めたり、個々の選手の能力を上げる必要がありそうです。
打率と得点の関係
plt.figure(figsize=(10, 5))
sns.scatterplot(data=df, x='打率', y='得点', hue='チーム名', palette=team_colors, s=100)
plt.title('打率と得点の関係')
plt.xlabel('打率')
plt.ylabel('得点')
plt.grid(True)
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()
for i, row in df.iterrows():
plt.text(
row['打率'], row['得点'], str(row['順位']), color='black',
fontsize=10, ha='right', va='bottom'
)
# 得点と失点の平均値を示す線を追加
plt.axvline(df['打率'].mean(), color='red', linestyle='--', label='打率の平均')
plt.axhline(df['得点'].mean(), color='blue', linestyle='--', label='得点の平均')
# 相関係数
correlation_coefficient = np.corrcoef(df['打率'], df['得点'])[0, 1]
plt.text(0.1, 0.9, f'r: {correlation_coefficient:.2f}', transform=plt.gca().transAxes)
# 回帰直線
slope, intercept = np.polyfit(df['打率'], df['得点'], 1)
x = np.linspace(min(df['打率']), max(df['打率']), 100)
y = slope * x + intercept
plt.plot(x, y, color='black', label=f'回帰直線: y = {slope:.2f}x + {intercept:.2f}', alpha=0.2)
plt.show()
縦軸に得点数、横軸に打率のグラフです。
相関係数は0.69で正の相関が見られます。
阪神は平均より打率が高く、得点数が多くなっています。DeNAや広島に近い打率ですが、阪神の方が得点数が30〜60も多く稼いでいます。また、打率は巨人に差をつけられていますが、得点数は阪神の方が多くなっています。長打や進塁打などで効率的に点を取っているのではないかと思われます。
平均線で区切られた右上にあるチームは阪神以外にもDeNAや巨人も位置しており、DeNAは阪神に近い打率を記録していますが、得点数が少ないため、来季は戦術を変えたり、選手の能力アップが求められるでしょう。
巨人はリーグ1位の打率を誇っていますが、得点数が3位となっているため、得点圏打率の向上など対策が必要でしょう。
平均線の右下への分布は、平均より打率が高く、得点が少ない、いわゆるタイムリー欠乏症のように思えます。2位の広島が位置しています。巨人も同様ですが、ヒットを放ち得点圏を作り出しているのは強みとして、本塁打や得点圏打率の増加をしてより効率的な攻撃ができるようになると良いのではないかと思います。
平均線の左上への分布は、平均より打率が低く、得点が多い、効率的に攻撃しているチームだと言えるでしょう。5位のヤクルトが位置しています。両翼が狭い神宮を本拠地にしたヤクルトなので長打に頼っているとも言えるかと思います。一方で近い打率を記録するDeNAや巨人と比べて得点の効率性が高く、良い打撃をしていると言えるでしょう。
平均線の左下への分布は、平均より打率が低く、得点が低いです。6位の中日が位置しています。前述の通り野手の放出により、影響が大きいのかと思われます。昨年最多安打の岡林選手や、打点と長打の多い細川選手などキー選手はいるので、全体のレベルアップを期待したいです。
スポナビさんの順位表からは出塁率が無いため、打率だけで見ると上記の通りとなりますが、別途出塁率から見ていると変わってくるかと思います。
防御率と勝利数の関係
plt.figure(figsize=(10, 5))
sns.scatterplot(data=df, x='防御率', y='勝利数', hue='チーム名', palette=team_colors, s=100)
plt.title('防御率と勝利数の関係')
plt.xlabel('防御率')
plt.ylabel('勝利数')
plt.grid(True)
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()
for i, row in df.iterrows():
plt.text(
row['防御率'], row['勝利数'], str(row['順位']), color='black',
fontsize=10, ha='right', va='bottom'
)
# 得点と失点の平均値を示す線を追加
plt.axvline(df['防御率'].mean(), color='red', linestyle='--', label='防御率の平均')
plt.axhline(df['勝利数'].mean(), color='blue', linestyle='--', label='勝利数の平均')
# 相関係数
correlation_coefficient = np.corrcoef(df['防御率'], df['勝利数'])[0, 1]
plt.text(0.1, 0.9, f'r: {correlation_coefficient:.2f}', transform=plt.gca().transAxes)
# 回帰直線
slope, intercept = np.polyfit(df['防御率'], df['勝利数'], 1)
x = np.linspace(min(df['防御率']), max(df['勝利数']), 100)
y = slope * x + intercept
plt.plot(x, y, color='black', label=f'回帰直線: y = {slope:.2f}x + {intercept:.2f}', alpha=0.2)
plt.show()
縦軸に勝利数、横軸に防御率のグラフです。
相関係数は-0.67で負の相関が見られます。
一部で防御率は、平均より数値が低い場合は、「良い」
平均より数値が高い場合は、「悪い」と表現させていただきます。
阪神はリーグ1位の防御率を誇っており、良い防御率が勝利の要因になっていると思えます。
平均線で区切られた左上への分布は、平均より防御率が低く、勝利数が多く、阪神の他に3位のDeNAが位置しています。DeNAの投手陣はリーグ全体で見ると点を取られにくいと言えます。
平均線の右上への分布は、平均より防御率が悪く、勝利数が多く、3位の広島と4位の巨人が位置しています。
平均線の左下への分布は、平均より防御率が低く、勝利数が少なく、6位の中日が位置しています。中日は最下位ですが、投手陣は良いのではないでしょうか。
平均線の右下への分布、平均より防御率が悪く、勝利数が少なく、5位のヤクルトが位置しています。防御率の悪さは、グラウンドの形状が話題に上がる神宮での試合が多いのか、純粋に投手陣に問題があるのか、ヤクルトの万年の悩みと言えそうです。
勝利数ではなく、勝率と防御率との相関係数は勝利数と同じでした。
防御率と失策の関係
plt.figure(figsize=(10, 5))
sns.scatterplot(data=df, x='失策', y='防御率', hue='チーム名', palette=team_colors, s=100)
plt.title('防御率と失策の関係')
plt.xlabel('失策')
plt.ylabel('防御率')
plt.grid(True)
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()
for i, row in df.iterrows():
plt.text(
row['失策'], row['防御率'], str(row['順位']), color='black',
fontsize=10, ha='right', va='bottom'
)
# 得点と失点の平均値を示す線を追加
plt.axvline(df['失策'].mean(), color='red', linestyle='--', label='失策の平均')
plt.axhline(df['防御率'].mean(), color='blue', linestyle='--', label='防御率の平均')
# 相関係数
correlation_coefficient = np.corrcoef(df['防御率'], df['失策'])[0, 1]
plt.text(0.1, 0.9, f'相関係数: {correlation_coefficient:.2f}', transform=plt.gca().transAxes)
# 回帰直線
slope, intercept = np.polyfit(df['失策'], df['防御率'], 1)
x = np.linspace(min(df['失策']), max(df['失策']), 100)
y = slope * x + intercept
plt.plot(x, y, color='black', label=f'回帰直線: y = {slope:.2f}x + {intercept:.2f}', alpha=0.2)
plt.show()
縦軸に勝利数、横軸に防御率のグラフです。
相関係数は-0.63で負の相関が見られます。
阪神は防御率が低いですが、失策が多くなっています。土のグラウンドでプレーしていることや、昨年も失策が多かったため守備の改善が大きく改善されていないことが要因となってくると思われます。
相関係数が-0.63で、これは失策が増えてくると防御率が低くなってくるのに弱い相関があると言えます。
一般的に失策数が多いと防御率が悪くなりますが、
データ数(チーム数)が少なかったり、阪神の失策数が多かったり、巨人が防御率の割に失策が少ない。ヤクルトの失策数が多いことにより負の相関が見えたのではないかと思います。
終わりに
「バウアー(YouTuber)」など冗談を絡めて書いているので記事全体を間に受けず居酒屋トークのように解釈してください。
また、1つの項目ごとに対象のデータで分析結果をまとめているので他の項目の観点は考慮していないです。
オマケ
・次の記事