乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

メジャーリーグ

今回は大谷選手の
ホームラン記録を可視化してみました。


解説動画はこちら



先日2024年09月20日
6打数3安打3ホームランなど
前人未到の記録を打ち立てた
ドジャース大谷選手

2004年シーズンの
今日までの成績を可視化してみました。


データの取得


可視化の元となるデータを取得します。

2024/09/21日現在
今季通算 603打数179安打 打率.297 52本塁打
122打点 125得点 52盗塁
という驚異的な成績です

この結果になるように加工していきます。

import requests
from bs4 import BeautifulSoup
import pandas as pd
import warnings
warnings.filterwarnings('ignore')

url = "https://times.abema.tv/articles/-/10018233"

res = requests.get(url)
soup = BeautifulSoup(res.content,"lxml")
table = soup.table

columns = ['日付', '対戦相手', '打順', '第1', '第2', '第3', '第4', '第5', '第6', '第7']
data = []
trs = table.find_all("tr")
for tr in trs[1:]:
    tds = tr.find_all(["td","th"])
    if "なし" in str(tds) or "中止" in str(tds):
        continue
    tmp = [td.text.replace("\xa0","").replace("\n","").replace("\r","").replace("\t","") for td in tds]
    tmp = tmp[0:3]+[t.replace("ニ","二").replace("2","二").replace("3","三") for t in tmp[3:]]
    if len(tmp)>=11:
        tmp = tmp[:10]
    data.append(tmp)

data2 = []
for d in data:
    t1,t2 = d[0:3],d[3:]
    for t in t2:
        if ""!=t:
            data2.append(t1+[t])

# データの確認
data2[0:2]
[['3月20日', 'パドレス', '2指', '遊ゴ'], ['3月20日', 'パドレス', '2指', '右安']]


これを集計しやすいようにデータフレームに加工していきます。
df = pd.DataFrame(data2,columns=["date","match","order","result"])

# 日本語の日付を変換する関数
def convert_date(date_str):
    month,day = date_str.replace("日","").split("月")
    return f"2024-{int(month):02}-{int(day):02}"

df['date'] = df['date'].apply(convert_date)
df['date'] = pd.to_datetime(df['date'])

# 本塁打
df['home_run'] = df['result'].apply(lambda x: 1 if '本' in x else 0)

# 安打
df['hit1'] = df['result'].apply(lambda x: 1 if '安' in x else 0)

# 2塁打
df['hit2'] = df['result'].apply(lambda x: 1 if x.endswith('二') else 0)

# 3塁打
df['hit3'] = df['result'].apply(lambda x: 1 if x.endswith('三') else 0)

# 指定された用語のリスト
terms = ['四球', '敬遠', '死球', '打妨', '中犠', '右犠', '左犠']

# 打数計算
df['flag'] = df['result'].apply(lambda x: 0 if x in terms else 1)

# 打席数合計
df['strokes'] = df['flag'].cumsum()

# ヒット数合計
df['hit_total'] = df[['hit1','hit2','hit3']].sum(axis=1).cumsum()

# ホームラン数合計
df['home_run_total'] = df['home_run'].cumsum()

# 打率
df['average'] = df[['hit_total','home_run_total']].sum(axis=1) / df['strokes']

# 603打数179安打 打率.297 52本塁打
df.tail()
index,date,match,order,result,home_run,hit1,hit2,hit3,flag,strokes,hit_total,home_run_total,average
688,2024-09-20 00:00:00,マーリンズ,1指,右本,1,0,0,0,1,599,125,51,0.2938230383973289
689,2024-09-21 00:00:00,ロッキーズ,1指,空振,0,0,0,0,1,600,125,51,0.29333333333333333
690,2024-09-21 00:00:00,ロッキーズ,1指,中安,0,1,0,0,1,601,126,51,0.2945091514143095
691,2024-09-21 00:00:00,ロッキーズ,1指,中本,1,0,0,0,1,602,126,52,0.2956810631229236
692,2024-09-21 00:00:00,ロッキーズ,1指,一安,0,1,0,0,1,603,127,52,0.296849087893864

これで同じ結果になりました。



Altairでプロットする

ここからこのデータを用いて
可視化を行います。

import pandas as pd
import altair as alt

# Altairで折れ線グラフを作成
chart = alt.Chart(df).mark_line().encode(
    x='date',
    y='average'
).properties(
    title='Data vs Average',
    width=800
)
chart.display()
visualization (1)



打率とホームラン数の推移を
2軸でプロットするとこうなります。
# averageの折れ線グラフ
average_line = alt.Chart(df).mark_line(color='blue').encode(
    x='date',
    y=alt.Y('average', axis=alt.Axis(title='Average'))
)

# home_run_totalの折れ線グラフ
home_run_line = alt.Chart(df).mark_line(color='red').encode(
    x='date',
    y=alt.Y('home_run_total', axis=alt.Axis(title='Home Run Total'))
)

# 二重軸のグラフを作成
chart = alt.layer(
    average_line,
    home_run_line
).resolve_scale(
    y='independent'
).properties(
    title='Data vs Average and Home Run Total',
    width=800
)
chart.display()
visualization


途中から急にホームラン数のピッチが上がっているようです。

ここからどれだけ伸びるのか
残り試合のホームラン数の予測もしてみます。


ホームラン数を時系列予測

残り試合は8試合です。
ARIMAという時系列予測モデルを用いて
予測をしてきます。

先ほどのデータフレームを加工して
予測データを作っていきます。

import pandas as pd
from statsmodels.tsa.arima.model import ARIMA
import matplotlib.pyplot as plt

# 日付をインデックスに設定
df2 = df.copy()
df2 = df.groupby('date').max()

# 欠損日を埋めて連続した日付にする
df2 = df2.asfreq('D')

# ARIMAモデルのフィッティング
model = ARIMA(df2['home_run_total'], order=(1, 1, 1))
model_fit = model.fit()

# 8日先までの予測
forecast = model_fit.forecast(steps=8)

# 結果のプロット
plt.figure(figsize=(12, 4))
plt.plot(df2.index, df2['home_run_total'], label='Actual')
plt.plot(pd.date_range(df2.index[-1] + pd.Timedelta(days=1), periods=8, freq='D'), forecast, label='Forecast', color='red')
plt.legend()
plt.show()
download

実測と予測を組み合わせて可視化しました。

残り8試合だとすると
およそ55本くらいまでは伸びそうです。

予測が外れて60本塁打くらいまで
行って欲しいですね!!!!

今回は大谷選手の打席結果を用いた
ホームラン成績の可視化でした

それでは。

今回は現在の成績から
大谷選手の2024シーズン結果を
予測してみました。

解説動画はこちら




はじめに

メジャーリーグ2024年シーズン
大谷選手の現在までの成績(05/25まで)
こちらを使って今シーズンの成績を予測します。



今シーズンの全打席結果が
掲載されているサイトが有ったため
そちらからデータを取得します。

掲載先

こちらによると現時点では
203打数69安打 打率.340
13本塁打 35打点 39得点 13盗塁
という成績でした。


現時点の成績

こちらの結果から分析すると
本塁打:0.05652173913043478
三塁打:0.008695652173913044 
二塁打:0.0782608695652174 
 安打:0.1565217391304348
 三振:0.1826086956521739
 四球:0.10869565217391304
アウト:0.40869565217391307
という確率になりました。

この確率を用いて
シミュレーションを行います。

メジャーリーグの年間平均打席数が650打席
これを1000シーズン行ったという設定です。

import random
import pandas as pd

# バッティング結果の確率を設定
batting_results = {
    "homerun": 0.05652173913043478, # 本塁打
    "triple": 0.008695652173913044, # 三塁打
    "double": 0.0782608695652174,  # 二塁打
    "single": 0.1565217391304348,  # 安打
    "strike": 0.1826086956521739, # 三振
    "walk": 0.10869565217391304, # 四球
    "out":  0.40869565217391307 # その他のアウト
}

def simulate_batting():
    rand = random.random()
    cumulative_probability = 0.0
    for result, probability in batting_results.items():
        cumulative_probability += probability
        if rand < cumulative_probability:
            return result
    return "out"

base = []
for i in range(1000):
    tmp = {k:0 for k in batting_results.keys()}
    n = 650
    for i in range(n):
        s = simulate_batting()
        if s in tmp:
            tmp[s]+=1
        else:
            tmp[s]=1
    base.append(tmp)

df = pd.DataFrame(base)
df["hits"] = df["homerun"] + df["triple"] + df["double"] + df["single"]
df.describe()

ここからホームランと
ヒット数を予測します。
mport pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# homerunの平均値と標準偏差を計算
mean = df['homerun'].mean()
std = df['homerun'].std()
print(f"{int(mean-std)}, {int(mean)}, {int(mean+std)}")

# ヒストグラムを表示
df['homerun'].hist(bins=20, alpha=0.7)

# 平均値、標準偏差±1のラインを描画
plt.axvline(x=mean, color='red', linestyle='--', label='mean')
plt.axvline(x=mean+std, color='green', linestyle='--', label='std+1')
plt.axvline(x=mean-std, color='blue', linestyle='--', label='std-1')

# 凡例を表示
plt.legend()
plt.show()
download-1

# hitsの平均値と標準偏差を計算
mean = df['hits'].mean()
std = df['hits'].std()
print(f"{int(mean-std)}, {int(mean)}, {int(mean+std)}")

# ヒストグラムを表示
df['hits'].hist(bins=20, alpha=0.7)

# 平均値、標準偏差±1のラインを描画
plt.axvline(x=mean, color='red', linestyle='--', label='mean')
plt.axvline(x=mean+std, color='green', linestyle='--', label='std+1')
plt.axvline(x=mean-std, color='blue', linestyle='--', label='std-1')

# 凡例を表示
plt.legend()
plt.show()
download


まとめ
05/25日までのペースだと
ホームラン数 : 36本(±6)
ヒット数 : 195本(±12)
あたりに落ち着く可能性が高いです。

年間40本塁打
200安打は少し、厳しいかもしれませんが
これからの活躍に期待したいですね


きょうはここまでです
それでは。

このページのトップヘ