イチロー選手9名のチームと松井秀喜選手9名のチーム、どちらが勝つかシミュレーションしました
見るだけですが野球ファンです、メジャーリーグも良く見ておりイチロー選手、松井秀喜選手の活躍していたころはyahooニュースなどで活躍をフォローしていました。両選手は世代も同じで比較されることの多い選手です。2004年にイチロー選手はシーズン最多安打を更新し262安打を記録、また打率も0.372を記録し首位打者のタイトルを獲得しています。松井選手の2004年もメジャーリーグ時代のシーズンキャリアハイではないかと言われています(本塁打31本、打率0.298、打点108)。
2004年のイチロー選手と松井秀喜選手の主要な成績を比較してみましょう。
イチロー選手(シアトル・マリナーズ):
打率: .372
安打数: 262
二塁打: 24
三塁打: 5
本塁打: 8
打点: 60
盗塁: 36
出塁率: .414
長打率: .455
OPS: .869
松井秀喜選手(ニューヨーク・ヤンキース):
打率: .298
安打数: 174
二塁打: 34
三塁打: 2
本塁打: 31
打点: 108
盗塁: 2
出塁率: .390
長打率: .522
OPS: .912
比較分析:
打率では、イチローが.372と非常に高い数字を記録したのに対し、松井は.298という優れた数字ではあるものの、イチローには及ばなかった。
長打力では、松井が31本塁打を放ち、長打率.522とパワーヒッターとしての力を発揮した。一方、イチローは8本塁打に留まったが、二塁打と三塁打を多く放ち、長打率は.455だった。
打点は、松井が108打点と100打点を超える活躍を見せたのに対し、イチローは60打点だった。これは松井が主に中軸打者として活躍したのに対し、イチローはリードオフマンとして出塁することに重きを置いていたことが影響していると考えられる。
出塁率は、イチローが.414、松井が.390とどちらも高い数字だった。イチローは高い打率に加えて四球も選べる選球眼の良さが出塁率に反映された。
盗塁数では、イチローが36個と圧倒的に多く、松井は2個だった。イチローは俊足を活かした走塁が持ち味の一つだった。
OPSでは、松井が.912とイチローの.869を上回った。これは松井の高い長打率が大きく影響している。
総合的に見ると、イチローは高い打率と出塁率、盗塁数で貢献し、松井は本塁打と打点で大きなインパクトを残しています。両者ともに素晴らしい成績を残したが、その活躍のスタイルは対照的だったと言えるかと思います。どちらも素晴らしい選手なのですがここでシミュレーションをしてみます、つまりイチロー選手が9名のチームと松井選手が9名のチーム、どちらが強いのかを比較してみます。
イチロー選手の得点力
1番から9番までイチロー選手だったらどれくらいの得点ができるでしょうか?理論値を求めることは難しいのでPythonを用いてシミュレーションしてみます。下記コードでは1打席ずつイチロー選手の2004年の成績をもとにシミュレーションし1回あたりの得点力を算出しています。シミュレーション回数は10,000回で平均得点数を求めてみます。
from numpy.random import choice
import numpy as np
# イチロー選手の成績に基づくパフォーマンス指標
at_bats = 704
singles = 225
doubles = 24
triples = 5
home_runs = 8
walks = 49 + 19 # 四球 + 敬遠
hit_by_pitch = 4
# 確率の計算
total_plate_appearances = at_bats + walks + hit_by_pitch
probabilities = {
'single': singles / total_plate_appearances,
'double': doubles / total_plate_appearances,
'triple': triples / total_plate_appearances,
'home_run': home_runs / total_plate_appearances,
'walk': walks / total_plate_appearances,
'hit_by_pitch': hit_by_pitch / total_plate_appearances,
'out': 1 - ((singles + doubles + triples + home_runs + walks + hit_by_pitch) / total_plate_appearances)
}
# イニングシミュレーション関数
def simulate_inning():
outs = 0
runs = 0
bases = np.array([0, 0, 0]) # ベースの状態:[一塁, 二塁, 三塁]
while outs < 3:
outcome = choice(list(probabilities.keys()), p=list(probabilities.values()))
if outcome == 'out':
outs += 1
elif outcome == 'single':
runs += bases[2]
bases = np.roll(bases, 1)
bases[0] = 1
elif outcome == 'double':
runs += bases[1:].sum()
bases = np.roll(bases, 2)
bases[0:2] = [0, 1]
elif outcome == 'triple':
runs += bases.sum()
bases = np.array([0, 0, 1])
elif outcome == 'home_run':
runs += bases.sum() + 1
bases = np.array([0, 0, 0])
elif outcome == 'walk' or outcome == 'hit_by_pitch':
if bases[0] == 1 and bases[1] == 1 and bases[2] == 1:
runs += 1
else:
for i in range(2, -1, -1):
if bases[i] == 1:
if i == 2:
runs += 1
else:
bases[i+1] = 1
bases[0] = 1
return runs
# シミュレーションの実行
n_simulations = 10000
total_runs = sum(simulate_inning() for _ in range(n_simulations))
average_runs = total_runs / n_simulations
# 結果の出力
print(f"イチロー選手の成績を持つバッターが9人いるチームの1回の攻撃での平均得点: {average_runs:.2f}点")
上記コードを実行すると0.70点であることがわかります。つまり1ゲーム9回までとすると6.3点得点することになります。
松井秀喜選手の得点力
同様に松井選手が1番から9番まで並んでいる打線の得点力をシミュレーションしてみます。
from numpy.random import choice
import numpy as np
# 松井選手の成績に基づくパフォーマンス指標
at_bats_matsui = 584
singles_matsui = 107
doubles_matsui = 34
triples_matsui = 2
home_runs_matsui = 31
walks_matsui = 88 + 2 # 四球 + 敬遠
hit_by_pitch_matsui = 3
# 確率の計算
total_plate_appearances_matsui = at_bats_matsui + walks_matsui + hit_by_pitch_matsui
probabilities_matsui = {
'single': singles_matsui / total_plate_appearances_matsui,
'double': doubles_matsui / total_plate_appearances_matsui,
'triple': triples_matsui / total_plate_appearances_matsui,
'home_run': home_runs_matsui / total_plate_appearances_matsui,
'walk': walks_matsui / total_plate_appearances_matsui,
'hit_by_pitch': hit_by_pitch_matsui / total_plate_appearances_matsui,
'out': 1 - ((singles_matsui + doubles_matsui + triples_matsui + home_runs_matsui + walks_matsui + hit_by_pitch_matsui) / total_plate_appearances_matsui)
}
# イニングシミュレーション関数
def simulate_inning_matsui():
outs = 0
runs = 0
bases = np.array([0, 0, 0]) # ベースの状態:[一塁, 二塁, 三塁]
while outs < 3:
outcome = choice(list(probabilities_matsui.keys()), p=list(probabilities_matsui.values()))
if outcome == 'out':
outs += 1
elif outcome == 'single':
runs += bases[2]
bases = np.roll(bases, 1)
bases[0] = 1
elif outcome == 'double':
runs += bases[1:].sum()
bases = np.roll(bases, 2)
bases[0:2] = [0, 1]
elif outcome == 'triple':
runs += bases.sum()
bases = np.array([0, 0, 1])
elif outcome == 'home_run':
runs += bases.sum() + 1
bases = np.array([0, 0, 0])
elif outcome == 'walk' or outcome == 'hit_by_pitch':
if bases[0] == 1 and bases[1] == 1 and bases[2] == 1:
runs += 1
else:
for i in range(2, -1, -1):
if bases[i] == 1:
if i == 2:
runs += 1
else:
bases[i+1] = 1
bases[0] = 1
return runs
# シミュレーションの実行
n_simulations = 10000
total_runs_matsui = sum(simulate_inning_matsui() for _ in range(n_simulations))
average_runs_matsui = total_runs_matsui / n_simulations
# 結果の出力
print(f"松井秀喜選手の成績を持つバッターが9人いるチームの1回の攻撃での平均得点: {average_runs_matsui:.2f}点")
10,000回のシミュレーションでは全員松井選手の場合に0.77点得点できることがわかります。つまり1ゲーム9回までとすると約6.9点得点できることになります。わずかに松井選手のほうが得点力がありそうです。
イチロー選手9名のチームと松井秀喜選手9名のチームで実際に対戦してもらう
先ほどのシミュレーションのコードを改変して実際にイチロー選手9名のチームと松井選手9名のチームで対戦してもらうようにコードを変えてみます。このコードではシーズン150試合として実際に試合をしてもらい、勝敗を計算しています。最初の10ゲームはゲームの実況中継をしてもらい最後にそれぞれの成績も算出しています。
from numpy.random import choice
import numpy as np
# イチロー選手の成績に基づくパフォーマンス指標
at_bats = 704
singles = 225
doubles = 24
triples = 5
home_runs = 8
walks = 49 + 19 # 四球 + 敬遠
hit_by_pitch = 4
# 確率の計算
total_plate_appearances = at_bats + walks + hit_by_pitch
probabilities = {
'single': singles / total_plate_appearances,
'double': doubles / total_plate_appearances,
'triple': triples / total_plate_appearances,
'home_run': home_runs / total_plate_appearances,
'walk': walks / total_plate_appearances,
'hit_by_pitch': hit_by_pitch / total_plate_appearances,
'out': 1 - ((singles + doubles + triples + home_runs + walks + hit_by_pitch) / total_plate_appearances)
}
# 松井選手の成績に基づくパフォーマンス指標
at_bats_matsui = 584
singles_matsui = 107
doubles_matsui = 34
triples_matsui = 2
home_runs_matsui = 31
walks_matsui = 88 + 2 # 四球 + 敬遠
hit_by_pitch_matsui = 3
# 確率の計算
total_plate_appearances_matsui = at_bats_matsui + walks_matsui + hit_by_pitch_matsui
probabilities_matsui = {
'single': singles_matsui / total_plate_appearances_matsui,
'double': doubles_matsui / total_plate_appearances_matsui,
'triple': triples_matsui / total_plate_appearances_matsui,
'home_run': home_runs_matsui / total_plate_appearances_matsui,
'walk': walks_matsui / total_plate_appearances_matsui,
'hit_by_pitch': hit_by_pitch_matsui / total_plate_appearances_matsui,
'out': 1 - ((singles_matsui + doubles_matsui + triples_matsui + home_runs_matsui + walks_matsui + hit_by_pitch_matsui) / total_plate_appearances_matsui)
}
# イニングシミュレーション関数
def simulate_inning(inning_number, broadcast=False, team_batting='Ichiro'):
outs = 0
runs = 0
bases = np.array([0, 0, 0]) # ベースの状態:[一塁, 二塁, 三塁]
inning_stats = {'at_bats': 0, 'hits': 0, 'doubles': 0, 'triples': 0, 'home_runs': 0, 'walks': 0, 'hit_by_pitch': 0}
probabilities_to_use = probabilities if team_batting == 'Ichiro' else probabilities_matsui
while outs < 3:
outcome = choice(list(probabilities_to_use.keys()), p=list(probabilities_to_use.values()))
if broadcast:
print(f"{inning_number}回{'' if team_batting == 'Ichiro' else '裏'} {team_batting}の攻撃 {outs}アウト ランナー:{bases} ", end="")
if outcome == 'out':
outs += 1
inning_stats['at_bats'] += 1
if broadcast:
print("アウト!")
elif outcome == 'single':
runs += bases[2]
bases = np.roll(bases, 1)
bases[0] = 1
inning_stats['hits'] += 1
inning_stats['at_bats'] += 1
if broadcast:
print("ヒット!")
elif outcome == 'double':
runs += bases[1:].sum()
bases = np.roll(bases, 2)
bases[0:2] = [0, 1]
inning_stats['hits'] += 1
inning_stats['doubles'] += 1
inning_stats['at_bats'] += 1
if broadcast:
print("ツーベースヒット!")
elif outcome == 'triple':
runs += bases.sum()
bases = np.array([0, 0, 1])
inning_stats['hits'] += 1
inning_stats['triples'] += 1
inning_stats['at_bats'] += 1
if broadcast:
print("スリーベースヒット!")
elif outcome == 'home_run':
runs += bases.sum() + 1
bases = np.array([0, 0, 0])
inning_stats['hits'] += 1
inning_stats['home_runs'] += 1
inning_stats['at_bats'] += 1
if broadcast:
print("ホームラン!")
elif outcome == 'walk' or outcome == 'hit_by_pitch':
if bases[0] == 1 and bases[1] == 1 and bases[2] == 1:
runs += 1
else:
for i in range(2, -1, -1):
if bases[i] == 1:
if i == 2:
runs += 1
else:
bases[i+1] = 1
bases[0] = 1
if outcome == 'walk':
inning_stats['walks'] += 1
else:
inning_stats['hit_by_pitch'] += 1
if broadcast:
print("四球!" if outcome == 'walk' else "死球!")
if broadcast:
print(f"{inning_number}回{'' if team_batting == 'Ichiro' else '裏'} {team_batting}の攻撃終了 {runs}点")
inning_stats['runs'] = runs
return inning_stats
# 150試合のシミュレーション
n_simulations = 150
n_broadcast_games = 10
ichiro_wins = 0
matsui_wins = 0
ichiro_total_stats = {'at_bats': 0, 'hits': 0, 'doubles': 0, 'triples': 0, 'home_runs': 0, 'walks': 0, 'hit_by_pitch': 0, 'runs': 0}
matsui_total_stats = {'at_bats': 0, 'hits': 0, 'doubles': 0, 'triples': 0, 'home_runs': 0, 'walks': 0, 'hit_by_pitch': 0, 'runs': 0}
for game in range(1, n_simulations + 1):
total_runs_ichiro = 0
total_runs_matsui = 0
broadcast = game <= n_broadcast_games
if broadcast:
print(f"\n第{game}試合:")
for inning in range(1, 10):
ichiro_inning_stats = simulate_inning(inning, broadcast, team_batting='Ichiro')
total_runs_ichiro += ichiro_inning_stats['runs']
for stat, value in ichiro_inning_stats.items():
if stat != 'home_runs':
ichiro_total_stats[stat] += value
else:
ichiro_total_stats[stat] += 1 if value > 0 else 0
if inning == 9 and total_runs_matsui > total_runs_ichiro:
break
matsui_inning_stats = simulate_inning(inning, broadcast, team_batting='Matsui')
total_runs_matsui += matsui_inning_stats['runs']
for stat, value in matsui_inning_stats.items():
if stat != 'home_runs':
matsui_total_stats[stat] += value
else:
matsui_total_stats[stat] += 1 if value > 0 else 0
if inning == 9 and total_runs_ichiro > total_runs_matsui:
break
if broadcast:
print(f"イチローチームの総得点: {total_runs_ichiro}点")
print(f"松井チームの総得点: {total_runs_matsui}点")
if total_runs_ichiro > total_runs_matsui:
ichiro_wins += 1
if broadcast:
print("イチローチームの勝利!")
elif total_runs_ichiro < total_runs_matsui:
matsui_wins += 1
if broadcast:
print("松井チームの勝利!")
else:
if broadcast:
print("引き分け!")
# 打率と出塁率の計算
ichiro_total_plate_appearances = ichiro_total_stats['at_bats'] + ichiro_total_stats['walks'] + ichiro_total_stats['hit_by_pitch']
ichiro_batting_average = ichiro_total_stats['hits'] / ichiro_total_stats['at_bats']
ichiro_on_base_percentage = (ichiro_total_stats['hits'] + ichiro_total_stats['walks'] + ichiro_total_stats['hit_by_pitch']) / ichiro_total_plate_appearances
matsui_total_plate_appearances = matsui_total_stats['at_bats'] + matsui_total_stats['walks'] + matsui_total_stats['hit_by_pitch']
matsui_batting_average = matsui_total_stats['hits'] / matsui_total_stats['at_bats']
matsui_on_base_percentage = (matsui_total_stats['hits'] + matsui_total_stats['walks'] + matsui_total_stats['hit_by_pitch']) / matsui_total_plate_appearances
# 長打率の計算
ichiro_slugging_percentage = (ichiro_total_stats['hits'] + ichiro_total_stats['doubles'] + 2 * ichiro_total_stats['triples'] + 3 * ichiro_total_stats['home_runs']) / ichiro_total_stats['at_bats']
matsui_slugging_percentage = (matsui_total_stats['hits'] + matsui_total_stats['doubles'] + 2 * matsui_total_stats['triples'] + 3 * matsui_total_stats['home_runs']) / matsui_total_stats['at_bats']
# ホームラン数を9で割る
ichiro_total_stats['home_runs'] //= 9
matsui_total_stats['home_runs'] //= 9
# 結果の出力
print(f"\n150試合終了後の勝敗:")
print(f"イチローチーム: {ichiro_wins}勝")
print(f"松井チーム: {matsui_wins}勝")
if ichiro_wins + matsui_wins < 150:
print(f"引き分け: {150 - ichiro_wins - matsui_wins}回")
print("\nイチロー選手の成績:")
print(f"打率: {ichiro_batting_average:.3f}")
print(f"出塁率: {ichiro_on_base_percentage:.3f}")
print(f"ホームラン数: {ichiro_total_stats['home_runs']}")
print(f"長打率: {ichiro_slugging_percentage:.3f}")
print("\n松井選手の成績:")
print(f"打率: {matsui_batting_average:.3f}")
print(f"出塁率: {matsui_on_base_percentage:.3f}")
print(f"ホームラン数: {matsui_total_stats['home_runs']}")
print(f"長打率: {matsui_slugging_percentage:.3f}")
実際にコードを実行するとまずゲームの実況中継が10試合行われます。1試合のみ見てみますと下記のようになっています。
第1試合:
1回 Ichiroの攻撃 0アウト ランナー:[0 0 0] アウト!
1回 Ichiroの攻撃 1アウト ランナー:[0 0 0] ヒット!
1回 Ichiroの攻撃 1アウト ランナー:[1 0 0] アウト!
1回 Ichiroの攻撃 2アウト ランナー:[1 0 0] アウト!
1回 Ichiroの攻撃終了 0点
1回裏 Matsuiの攻撃 0アウト ランナー:[0 0 0] 四球!
1回裏 Matsuiの攻撃 0アウト ランナー:[1 0 0] アウト!
1回裏 Matsuiの攻撃 1アウト ランナー:[1 0 0] ツーベースヒット!
1回裏 Matsuiの攻撃 1アウト ランナー:[0 1 1] アウト!
1回裏 Matsuiの攻撃 2アウト ランナー:[0 1 1] ヒット!
1回裏 Matsuiの攻撃 2アウト ランナー:[1 0 1] ヒット!
1回裏 Matsuiの攻撃 2アウト ランナー:[1 1 0] 四球!
1回裏 Matsuiの攻撃 2アウト ランナー:[1 1 1] ヒット!
1回裏 Matsuiの攻撃 2アウト ランナー:[1 1 1] ホームラン!
1回裏 Matsuiの攻撃 2アウト ランナー:[0 0 0] アウト!
1回裏 Matsuiの攻撃終了 7点
2回 Ichiroの攻撃 0アウト ランナー:[0 0 0] アウト!
2回 Ichiroの攻撃 1アウト ランナー:[0 0 0] スリーベースヒット!
2回 Ichiroの攻撃 1アウト ランナー:[0 0 1] ヒット!
2回 Ichiroの攻撃 1アウト ランナー:[1 0 0] ヒット!
2回 Ichiroの攻撃 1アウト ランナー:[1 1 0] ヒット!
2回 Ichiroの攻撃 1アウト ランナー:[1 1 1] アウト!
2回 Ichiroの攻撃 2アウト ランナー:[1 1 1] 四球!
2回 Ichiroの攻撃 2アウト ランナー:[1 1 1] アウト!
2回 Ichiroの攻撃終了 2点
2回裏 Matsuiの攻撃 0アウト ランナー:[0 0 0] ヒット!
2回裏 Matsuiの攻撃 0アウト ランナー:[1 0 0] アウト!
2回裏 Matsuiの攻撃 1アウト ランナー:[1 0 0] アウト!
2回裏 Matsuiの攻撃 2アウト ランナー:[1 0 0] ヒット!
2回裏 Matsuiの攻撃 2アウト ランナー:[1 1 0] アウト!
2回裏 Matsuiの攻撃終了 0点
3回 Ichiroの攻撃 0アウト ランナー:[0 0 0] ツーベースヒット!
3回 Ichiroの攻撃 0アウト ランナー:[0 1 0] アウト!
3回 Ichiroの攻撃 1アウト ランナー:[0 1 0] アウト!
3回 Ichiroの攻撃 2アウト ランナー:[0 1 0] ツーベースヒット!
3回 Ichiroの攻撃 2アウト ランナー:[0 1 0] ヒット!
3回 Ichiroの攻撃 2アウト ランナー:[1 0 1] アウト!
3回 Ichiroの攻撃終了 1点
3回裏 Matsuiの攻撃 0アウト ランナー:[0 0 0] アウト!
3回裏 Matsuiの攻撃 1アウト ランナー:[0 0 0] アウト!
3回裏 Matsuiの攻撃 2アウト ランナー:[0 0 0] アウト!
3回裏 Matsuiの攻撃終了 0点
4回 Ichiroの攻撃 0アウト ランナー:[0 0 0] アウト!
4回 Ichiroの攻撃 1アウト ランナー:[0 0 0] ヒット!
4回 Ichiroの攻撃 1アウト ランナー:[1 0 0] アウト!
4回 Ichiroの攻撃 2アウト ランナー:[1 0 0] アウト!
4回 Ichiroの攻撃終了 0点
4回裏 Matsuiの攻撃 0アウト ランナー:[0 0 0] アウト!
4回裏 Matsuiの攻撃 1アウト ランナー:[0 0 0] 四球!
4回裏 Matsuiの攻撃 1アウト ランナー:[1 0 0] アウト!
4回裏 Matsuiの攻撃 2アウト ランナー:[1 0 0] 四球!
4回裏 Matsuiの攻撃 2アウト ランナー:[1 1 0] アウト!
4回裏 Matsuiの攻撃終了 0点
5回 Ichiroの攻撃 0アウト ランナー:[0 0 0] ヒット!
5回 Ichiroの攻撃 0アウト ランナー:[1 0 0] アウト!
5回 Ichiroの攻撃 1アウト ランナー:[1 0 0] アウト!
5回 Ichiroの攻撃 2アウト ランナー:[1 0 0] アウト!
5回 Ichiroの攻撃終了 0点
5回裏 Matsuiの攻撃 0アウト ランナー:[0 0 0] アウト!
5回裏 Matsuiの攻撃 1アウト ランナー:[0 0 0] 四球!
5回裏 Matsuiの攻撃 1アウト ランナー:[1 0 0] ツーベースヒット!
5回裏 Matsuiの攻撃 1アウト ランナー:[0 1 1] 四球!
5回裏 Matsuiの攻撃 1アウト ランナー:[1 1 1] アウト!
5回裏 Matsuiの攻撃 2アウト ランナー:[1 1 1] ホームラン!
5回裏 Matsuiの攻撃 2アウト ランナー:[0 0 0] ヒット!
5回裏 Matsuiの攻撃 2アウト ランナー:[1 0 0] ホームラン!
5回裏 Matsuiの攻撃 2アウト ランナー:[0 0 0] ヒット!
5回裏 Matsuiの攻撃 2アウト ランナー:[1 0 0] ヒット!
5回裏 Matsuiの攻撃 2アウト ランナー:[1 1 0] アウト!
5回裏 Matsuiの攻撃終了 7点
6回 Ichiroの攻撃 0アウト ランナー:[0 0 0] アウト!
6回 Ichiroの攻撃 1アウト ランナー:[0 0 0] アウト!
6回 Ichiroの攻撃 2アウト ランナー:[0 0 0] ヒット!
6回 Ichiroの攻撃 2アウト ランナー:[1 0 0] ヒット!
6回 Ichiroの攻撃 2アウト ランナー:[1 1 0] アウト!
6回 Ichiroの攻撃終了 0点
6回裏 Matsuiの攻撃 0アウト ランナー:[0 0 0] アウト!
6回裏 Matsuiの攻撃 1アウト ランナー:[0 0 0] アウト!
6回裏 Matsuiの攻撃 2アウト ランナー:[0 0 0] 四球!
6回裏 Matsuiの攻撃 2アウト ランナー:[1 0 0] アウト!
6回裏 Matsuiの攻撃終了 0点
7回 Ichiroの攻撃 0アウト ランナー:[0 0 0] ヒット!
7回 Ichiroの攻撃 0アウト ランナー:[1 0 0] 四球!
7回 Ichiroの攻撃 0アウト ランナー:[1 1 0] アウト!
7回 Ichiroの攻撃 1アウト ランナー:[1 1 0] アウト!
7回 Ichiroの攻撃 2アウト ランナー:[1 1 0] アウト!
7回 Ichiroの攻撃終了 0点
7回裏 Matsuiの攻撃 0アウト ランナー:[0 0 0] 四球!
7回裏 Matsuiの攻撃 0アウト ランナー:[1 0 0] アウト!
7回裏 Matsuiの攻撃 1アウト ランナー:[1 0 0] アウト!
7回裏 Matsuiの攻撃 2アウト ランナー:[1 0 0] アウト!
7回裏 Matsuiの攻撃終了 0点
8回 Ichiroの攻撃 0アウト ランナー:[0 0 0] アウト!
8回 Ichiroの攻撃 1アウト ランナー:[0 0 0] ヒット!
8回 Ichiroの攻撃 1アウト ランナー:[1 0 0] アウト!
8回 Ichiroの攻撃 2アウト ランナー:[1 0 0] ヒット!
8回 Ichiroの攻撃 2アウト ランナー:[1 1 0] アウト!
8回 Ichiroの攻撃終了 0点
8回裏 Matsuiの攻撃 0アウト ランナー:[0 0 0] ヒット!
8回裏 Matsuiの攻撃 0アウト ランナー:[1 0 0] アウト!
8回裏 Matsuiの攻撃 1アウト ランナー:[1 0 0] 四球!
8回裏 Matsuiの攻撃 1アウト ランナー:[1 1 0] アウト!
8回裏 Matsuiの攻撃 2アウト ランナー:[1 1 0] ツーベースヒット!
8回裏 Matsuiの攻撃 2アウト ランナー:[0 1 1] アウト!
8回裏 Matsuiの攻撃終了 1点
9回 Ichiroの攻撃 0アウト ランナー:[0 0 0] アウト!
9回 Ichiroの攻撃 1アウト ランナー:[0 0 0] アウト!
9回 Ichiroの攻撃 2アウト ランナー:[0 0 0] ヒット!
9回 Ichiroの攻撃 2アウト ランナー:[1 0 0] ヒット!
9回 Ichiroの攻撃 2アウト ランナー:[1 1 0] アウト!
9回 Ichiroの攻撃終了 0点
イチローチームの総得点: 3点
松井チームの総得点: 15点
松井チームの勝利!
第一試合は15対3で松井チームの勝利、こんな風に10試合を実況した後で総合成績が求まります。
150試合終了後の勝敗:
イチローチーム: 60勝
松井チーム: 77勝
引き分け: 13回
シミュレーションごとにばらつきはあるのですが概ね松井選手9名のチームが勝ち越すことがほとんどです。
最後に150試合を振り返ってそれぞれの選手の成績が出てきます。今回のシミュレーショでは下記のような成績になりました。
イチロー選手の成績:
打率: 0.367
出塁率: 0.427
ホームラン数: 8
長打率: 0.448
松井選手の成績:
打率: 0.303
出塁率: 0.396
ホームラン数: 28
長打率: 0.508
まとめ
同じ選手が1~9番まで並ぶ、という設定では松井選手のほうがわずかに優れている、という結果になりました。
ただし、このシミュレーションにはいくつかの限界があることに注意が必要です。まず、盗塁、進塁の能力も考慮されていません。さらには守備力、投手の影響などが考慮されていません。また、選手の調子の波や対戦相手の影響なども反映されていません。あくまでも2004年の成績を基にしたシンプルなモデルでのシミュレーションであることを理解しておく必要があります。
とはいえ、このシミュレーションは、イチロー選手と松井選手の打撃スタイルの違いを面白く比較できる試みだと言えるでしょう。ネットで調べても同じような試みはみつけられませんでした。
イチロー選手と松井選手、二人のレジェンドの対決。そんな夢のようなシナリオを、データ分析の力で実現してみるのも面白いのではないでしょうか。
イチロー選手の足の速さを考慮していないのではないか?
上記のシミュレーションはイチロー選手の足の速さを考慮していないのはないか、との疑問もあるため下記の様にコードを改変しました。
# 盗塁の試行(イチローチームのみ、確率を20%に変更)
if team_batting == 'Ichiro' and bases[0] == 1 and bases[1] == 0 and np.random.random() < 0.2:
if np.random.random() < stolen_base_success_rate:
bases[1] = 1
bases[0] = 0
inning_stats['stolen_bases'] += 1
if broadcast:
print("盗塁成功!")
else:
outs += 1
bases[0] = 0
if broadcast:
print("盗塁失敗!")
イチローチームの打席で、1塁にランナーがいて2塁が空いている場合、20%の確率で盗塁を試行するようにしました。盗塁成功率は、イチロー選手の2004年の成績である36/47(約76.6%)を使用しています。盗塁に失敗した場合は、もちろんアウトになります。
elif outcome == 'single':
runs += bases[2]
if bases[1] == 1:
if (team_batting == 'Ichiro' and np.random.random() < 0.5) or (team_batting == 'Matsui' and np.random.random() < 0.25):
runs += 1
bases = np.array([1, 0, 0])
else:
bases = np.roll(bases, 1)
bases[0] = 1
else:
bases = np.roll(bases, 1)
bases[0] = 1
inning_stats['hits'] += 1
inning_stats['at_bats'] += 1
if broadcast:
print("ヒット!")
イチローチームの打席で、シングルヒットが出た際に2塁ランナーがいる場合、50%の確率で2塁ランナーがホームまで進塁するようにしました。一方、松井選手のチームでは、同様の状況で25%の確率で2塁ランナーがホームまで進塁します。これら改変を組み入れたコード全体は下記のようになります。
from numpy.random import choice
import numpy as np
# イチロー選手の成績に基づくパフォーマンス指標(変更なし)
at_bats = 704
singles = 225
doubles = 24
triples = 5
home_runs = 8
walks = 49 + 19 # 四球 + 敬遠
hit_by_pitch = 4
stolen_bases = 36
caught_stealing = 11
# 確率の計算(変更なし)
total_plate_appearances = at_bats + walks + hit_by_pitch
probabilities = {
'single': singles / total_plate_appearances,
'double': doubles / total_plate_appearances,
'triple': triples / total_plate_appearances,
'home_run': home_runs / total_plate_appearances,
'walk': walks / total_plate_appearances,
'hit_by_pitch': hit_by_pitch / total_plate_appearances,
'out': 1 - ((singles + doubles + triples + home_runs + walks + hit_by_pitch) / total_plate_appearances)
}
# 盗塁成功率の計算(変更なし)
stolen_base_success_rate = stolen_bases / (stolen_bases + caught_stealing)
# 松井選手の成績に基づくパフォーマンス指標(変更なし)
at_bats_matsui = 584
singles_matsui = 107
doubles_matsui = 34
triples_matsui = 2
home_runs_matsui = 31
walks_matsui = 88 + 2 # 四球 + 敬遠
hit_by_pitch_matsui = 3
# 確率の計算(変更なし)
total_plate_appearances_matsui = at_bats_matsui + walks_matsui + hit_by_pitch_matsui
probabilities_matsui = {
'single': singles_matsui / total_plate_appearances_matsui,
'double': doubles_matsui / total_plate_appearances_matsui,
'triple': triples_matsui / total_plate_appearances_matsui,
'home_run': home_runs_matsui / total_plate_appearances_matsui,
'walk': walks_matsui / total_plate_appearances_matsui,
'hit_by_pitch': hit_by_pitch_matsui / total_plate_appearances_matsui,
'out': 1 - ((singles_matsui + doubles_matsui + triples_matsui + home_runs_matsui + walks_matsui + hit_by_pitch_matsui) / total_plate_appearances_matsui)
}
# イニングシミュレーション関数
def simulate_inning(inning_number, broadcast=False, team_batting='Ichiro'):
outs = 0
runs = 0
bases = np.array([0, 0, 0]) # ベースの状態:[一塁, 二塁, 三塁]
inning_stats = {'at_bats': 0, 'hits': 0, 'doubles': 0, 'triples': 0, 'home_runs': 0, 'walks': 0, 'hit_by_pitch': 0, 'stolen_bases': 0}
probabilities_to_use = probabilities if team_batting == 'Ichiro' else probabilities_matsui
while outs < 3:
outcome = choice(list(probabilities_to_use.keys()), p=list(probabilities_to_use.values()))
if broadcast:
print(f"{inning_number}回{'' if team_batting == 'Ichiro' else '裏'} {team_batting}の攻撃 {outs}アウト ランナー:{bases} ", end="")
if outcome == 'out':
outs += 1
inning_stats['at_bats'] += 1
if broadcast:
print("アウト!")
elif outcome == 'single':
runs += bases[2]
if bases[1] == 1:
if (team_batting == 'Ichiro' and np.random.random() < 0.5) or (team_batting == 'Matsui' and np.random.random() < 0.25):
runs += 1
bases = np.array([1, 0, 0])
else:
bases = np.roll(bases, 1)
bases[0] = 1
else:
bases = np.roll(bases, 1)
bases[0] = 1
inning_stats['hits'] += 1
inning_stats['at_bats'] += 1
if broadcast:
print("ヒット!")
elif outcome == 'double':
runs += bases[1:].sum()
bases = np.roll(bases, 2)
bases[0:2] = [0, 1]
inning_stats['hits'] += 1
inning_stats['doubles'] += 1
inning_stats['at_bats'] += 1
if broadcast:
print("ツーベースヒット!")
elif outcome == 'triple':
runs += bases.sum()
bases = np.array([0, 0, 1])
inning_stats['hits'] += 1
inning_stats['triples'] += 1
inning_stats['at_bats'] += 1
if broadcast:
print("スリーベースヒット!")
elif outcome == 'home_run':
runs += bases.sum() + 1
bases = np.array([0, 0, 0])
inning_stats['hits'] += 1
inning_stats['home_runs'] += 1
inning_stats['at_bats'] += 1
if broadcast:
print("ホームラン!")
elif outcome == 'walk' or outcome == 'hit_by_pitch':
if bases[0] == 1 and bases[1] == 1 and bases[2] == 1:
runs += 1
else:
for i in range(2, -1, -1):
if bases[i] == 1:
if i == 2:
runs += 1
else:
bases[i+1] = 1
bases[0] = 1
if outcome == 'walk':
inning_stats['walks'] += 1
else:
inning_stats['hit_by_pitch'] += 1
if broadcast:
print("四球!" if outcome == 'walk' else "死球!")
# 盗塁の試行(イチローチームのみ、確率を20%に変更)
if team_batting == 'Ichiro' and bases[0] == 1 and bases[1] == 0 and np.random.random() < 0.2:
if np.random.random() < stolen_base_success_rate:
bases[1] = 1
bases[0] = 0
inning_stats['stolen_bases'] += 1
if broadcast:
print("盗塁成功!")
else:
outs += 1
bases[0] = 0
if broadcast:
print("盗塁失敗!")
if broadcast:
print(f"{inning_number}回{'' if team_batting == 'Ichiro' else '裏'} {team_batting}の攻撃終了 {runs}点")
inning_stats['runs'] = runs
return inning_stats
# 150試合のシミュレーション(変更なし)
n_simulations = 150
n_broadcast_games = 10
ichiro_wins = 0
matsui_wins = 0
ichiro_total_stats = {'at_bats': 0, 'hits': 0, 'doubles': 0, 'triples': 0, 'home_runs': 0, 'walks': 0, 'hit_by_pitch': 0, 'runs': 0, 'stolen_bases': 0}
matsui_total_stats = {'at_bats': 0, 'hits': 0, 'doubles': 0, 'triples': 0, 'home_runs': 0, 'walks': 0, 'hit_by_pitch': 0, 'runs': 0, 'stolen_bases': 0}
for game in range(1, n_simulations + 1):
total_runs_ichiro = 0
total_runs_matsui = 0
broadcast = game <= n_broadcast_games
if broadcast:
print(f"\n第{game}試合:")
for inning in range(1, 10):
ichiro_inning_stats = simulate_inning(inning, broadcast, team_batting='Ichiro')
total_runs_ichiro += ichiro_inning_stats['runs']
for stat, value in ichiro_inning_stats.items():
if stat != 'home_runs':
ichiro_total_stats[stat] += value
else:
ichiro_total_stats[stat] += 1 if value > 0 else 0
if inning == 9 and total_runs_matsui > total_runs_ichiro:
break
matsui_inning_stats = simulate_inning(inning, broadcast, team_batting='Matsui')
total_runs_matsui += matsui_inning_stats['runs']
for stat, value in matsui_inning_stats.items():
if stat != 'home_runs':
matsui_total_stats[stat] += value
else:
matsui_total_stats[stat] += 1 if value > 0 else 0
if inning == 9 and total_runs_ichiro > total_runs_matsui:
break
if broadcast:
print(f"イチローチームの総得点: {total_runs_ichiro}点")
print(f"松井チームの総得点: {total_runs_matsui}点")
if total_runs_ichiro > total_runs_matsui:
ichiro_wins += 1
if broadcast:
print("イチローチームの勝利!")
elif total_runs_ichiro < total_runs_matsui:
matsui_wins += 1
if broadcast:
print("松井チームの勝利!")
else:
if broadcast:
print("引き分け!")
# 打率と出塁率の計算(変更なし)
ichiro_total_plate_appearances = ichiro_total_stats['at_bats'] + ichiro_total_stats['walks'] + ichiro_total_stats['hit_by_pitch']
ichiro_batting_average = ichiro_total_stats['hits'] / ichiro_total_stats['at_bats']
ichiro_on_base_percentage = (ichiro_total_stats['hits'] + ichiro_total_stats['walks'] + ichiro_total_stats['hit_by_pitch']) / ichiro_total_plate_appearances
matsui_total_plate_appearances = matsui_total_stats['at_bats'] + matsui_total_stats['walks'] + matsui_total_stats['hit_by_pitch']
matsui_batting_average = matsui_total_stats['hits'] / matsui_total_stats['at_bats']
matsui_on_base_percentage = (matsui_total_stats['hits'] + matsui_total_stats['walks'] + matsui_total_stats['hit_by_pitch']) / matsui_total_plate_appearances
# 長打率の計算(変更なし)
ichiro_slugging_percentage = (ichiro_total_stats['hits'] + ichiro_total_stats['doubles'] + 2 * ichiro_total_stats['triples'] + 3 * ichiro_total_stats['home_runs']) / ichiro_total_stats['at_bats']
matsui_slugging_percentage = (matsui_total_stats['hits'] + matsui_total_stats['doubles'] + 2 * matsui_total_stats['triples'] + 3 * matsui_total_stats['home_runs']) / matsui_total_stats['at_bats']
# ホームラン数と盗塁数を9で割る
ichiro_total_stats['home_runs'] //= 9
ichiro_total_stats['stolen_bases'] //= 9
matsui_total_stats['home_runs'] //= 9
# 結果の出力
print(f"\n150試合終了後の勝敗:")
print(f"イチローチーム: {ichiro_wins}勝")
print(f"松井チーム: {matsui_wins}勝")
if ichiro_wins + matsui_wins < 150:
print(f"引き分け: {150 - ichiro_wins - matsui_wins}回")
print("\nイチロー選手の成績:")
print(f"打率: {ichiro_batting_average:.3f}")
print(f"出塁率: {ichiro_on_base_percentage:.3f}")
print(f"ホームラン数: {ichiro_total_stats['home_runs']}")
print(f"長打率: {ichiro_slugging_percentage:.3f}")
print(f"盗塁数: {ichiro_total_stats['stolen_bases']}")
print("\n松井選手の成績:")
print(f"打率: {matsui_batting_average:.3f}")
print(f"出塁率: {matsui_on_base_percentage:.3f}")
print(f"ホームラン数: {matsui_total_stats['home_runs']}")
print(f"長打率: {matsui_slugging_percentage:.3f}")
結果はどうでしょうか?下記のようにイチロー選手のチームが勝ち越すことも出てくるようになりました。
150試合終了後の勝敗:
イチローチーム: 77勝 松井チーム: 60勝 引き分け: 13回
イチロー選手の成績:
打率: 0.390 出塁率: 0.448 ホームラン数: 7 長打率: 0.466 盗塁数: 48
松井選手の成績:
打率: 0.297 出塁率: 0.388 ホームラン数: 28 長打率: 0.499
1シーズン150試合、100シーズンしてみます。コードは下記になります。
from numpy.random import choice
import numpy as np
import matplotlib.pyplot as plt
# イチロー選手の成績に基づくパフォーマンス指標(変更なし)
at_bats = 704
singles = 225
doubles = 24
triples = 5
home_runs = 8
walks = 49 + 19 # 四球 + 敬遠
hit_by_pitch = 4
stolen_bases = 36
caught_stealing = 11
# 確率の計算(変更なし)
total_plate_appearances = at_bats + walks + hit_by_pitch
probabilities = {
'single': singles / total_plate_appearances,
'double': doubles / total_plate_appearances,
'triple': triples / total_plate_appearances,
'home_run': home_runs / total_plate_appearances,
'walk': walks / total_plate_appearances,
'hit_by_pitch': hit_by_pitch / total_plate_appearances,
'out': 1 - ((singles + doubles + triples + home_runs + walks + hit_by_pitch) / total_plate_appearances)
}
# 盗塁成功率の計算(変更なし)
stolen_base_success_rate = stolen_bases / (stolen_bases + caught_stealing)
# 松井選手の成績に基づくパフォーマンス指標(変更なし)
at_bats_matsui = 584
singles_matsui = 107
doubles_matsui = 34
triples_matsui = 2
home_runs_matsui = 31
walks_matsui = 88 + 2 # 四球 + 敬遠
hit_by_pitch_matsui = 3
# 確率の計算(変更なし)
total_plate_appearances_matsui = at_bats_matsui + walks_matsui + hit_by_pitch_matsui
probabilities_matsui = {
'single': singles_matsui / total_plate_appearances_matsui,
'double': doubles_matsui / total_plate_appearances_matsui,
'triple': triples_matsui / total_plate_appearances_matsui,
'home_run': home_runs_matsui / total_plate_appearances_matsui,
'walk': walks_matsui / total_plate_appearances_matsui,
'hit_by_pitch': hit_by_pitch_matsui / total_plate_appearances_matsui,
'out': 1 - ((singles_matsui + doubles_matsui + triples_matsui + home_runs_matsui + walks_matsui + hit_by_pitch_matsui) / total_plate_appearances_matsui)
}
# イニングシミュレーション関数(実況なし)
def simulate_inning(team_batting='Ichiro'):
outs = 0
runs = 0
bases = np.array([0, 0, 0]) # ベースの状態:[一塁, 二塁, 三塁]
inning_stats = {'at_bats': 0, 'hits': 0, 'doubles': 0, 'triples': 0, 'home_runs': 0, 'walks': 0, 'hit_by_pitch': 0, 'stolen_bases': 0}
probabilities_to_use = probabilities if team_batting == 'Ichiro' else probabilities_matsui
while outs < 3:
outcome = choice(list(probabilities_to_use.keys()), p=list(probabilities_to_use.values()))
if outcome == 'out':
outs += 1
inning_stats['at_bats'] += 1
elif outcome == 'single':
runs += bases[2]
if bases[1] == 1:
if (team_batting == 'Ichiro' and np.random.random() < 0.5) or (team_batting == 'Matsui' and np.random.random() < 0.25):
runs += 1
bases = np.array([1, 0, 0])
else:
bases = np.roll(bases, 1)
bases[0] = 1
else:
bases = np.roll(bases, 1)
bases[0] = 1
inning_stats['hits'] += 1
inning_stats['at_bats'] += 1
elif outcome == 'double':
runs += bases[1:].sum()
bases = np.roll(bases, 2)
bases[0:2] = [0, 1]
inning_stats['hits'] += 1
inning_stats['doubles'] += 1
inning_stats['at_bats'] += 1
elif outcome == 'triple':
runs += bases.sum()
bases = np.array([0, 0, 1])
inning_stats['hits'] += 1
inning_stats['triples'] += 1
inning_stats['at_bats'] += 1
elif outcome == 'home_run':
runs += bases.sum() + 1
bases = np.array([0, 0, 0])
inning_stats['hits'] += 1
inning_stats['home_runs'] += 1
inning_stats['at_bats'] += 1
elif outcome == 'walk' or outcome == 'hit_by_pitch':
if bases[0] == 1 and bases[1] == 1 and bases[2] == 1:
runs += 1
else:
for i in range(2, -1, -1):
if bases[i] == 1:
if i == 2:
runs += 1
else:
bases[i+1] = 1
bases[0] = 1
if outcome == 'walk':
inning_stats['walks'] += 1
else:
inning_stats['hit_by_pitch'] += 1
# 盗塁の試行(イチローチームのみ、確率を20%に変更)
if team_batting == 'Ichiro' and bases[0] == 1 and bases[1] == 0 and np.random.random() < 0.2:
if np.random.random() < stolen_base_success_rate:
bases[1] = 1
bases[0] = 0
inning_stats['stolen_bases'] += 1
else:
outs += 1
bases[0] = 0
inning_stats['runs'] = runs
return inning_stats
# 100シーズンのシミュレーション
n_seasons = 100
n_games_per_season = 150
ichiro_seasons_won = 0
matsui_seasons_won = 0
for season in range(n_seasons):
ichiro_wins = 0
matsui_wins = 0
ichiro_season_stats = {'at_bats': 0, 'hits': 0, 'doubles': 0, 'triples': 0, 'home_runs': 0, 'walks': 0, 'hit_by_pitch': 0, 'runs': 0, 'stolen_bases': 0}
matsui_season_stats = {'at_bats': 0, 'hits': 0, 'doubles': 0, 'triples': 0, 'home_runs': 0, 'walks': 0, 'hit_by_pitch': 0, 'runs': 0, 'stolen_bases': 0}
for game in range(n_games_per_season):
total_runs_ichiro = 0
total_runs_matsui = 0
for inning in range(1, 10):
ichiro_inning_stats = simulate_inning(team_batting='Ichiro')
total_runs_ichiro += ichiro_inning_stats['runs']
for stat, value in ichiro_inning_stats.items():
ichiro_season_stats[stat] += value
if inning == 9 and total_runs_matsui > total_runs_ichiro:
break
matsui_inning_stats = simulate_inning(team_batting='Matsui')
total_runs_matsui += matsui_inning_stats['runs']
for stat, value in matsui_inning_stats.items():
matsui_season_stats[stat] += value
if inning == 9 and total_runs_ichiro > total_runs_matsui:
break
if total_runs_ichiro > total_runs_matsui:
ichiro_wins += 1
elif total_runs_ichiro < total_runs_matsui:
matsui_wins += 1
if ichiro_wins > matsui_wins:
ichiro_seasons_won += 1
elif ichiro_wins < matsui_wins:
matsui_seasons_won += 1
# 結果の出力
print(f"\n100シーズンのシミュレーション結果:")
print(f"イチローが勝ったシーズン数: {ichiro_seasons_won}")
print(f"松井が勝ったシーズン数: {matsui_seasons_won}")
if ichiro_seasons_won + matsui_seasons_won < n_seasons:
print(f"引き分けのシーズン数: {n_seasons - ichiro_seasons_won - matsui_seasons_won}")
100シーズンのシミュレーション結果:
イチローチームが勝ったシーズン数: 40
松井チームが勝ったシーズン数: 57
引き分けのシーズン数: 3
という結果です、走塁能力を加味しても松井選手のチームのほうが勝率は高そうという結果でした。
コード作成について:Chat GPT4 vs. Claude 3
なお本記事のコードはほぼすべてClaude3あるいはChat GPT4を用いて作成しました。2024年3月23日の時点でどちらもこちらがきちんとオーダーするとバグはほとんどなくコードを作成してくれます。ただしClaude 3のほうが再現性があるコードを書いてくれるようで使いやすかったです。Chat GPT4は同じことをオーダーしても毎回違ったコードを書いてくるので微修正をするたびにコードが変わってしまい解読するのが大変です。その点、Claude 3は同じオーダーに対してはほぼ同じコードを返してくれますし一貫性があるように思いました。ただしClaude 3は実際にコードを実行する能力がなかったり、オーダーを修正して再度やり直させるのが難しかったりで一長一短でした。