乙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安打は少し、厳しいかもしれませんが
これからの活躍に期待したいですね


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

今回はMLB2023の大谷投手の
全投球データを見てみることとしました。

解説動画はこちら




データの入手先

今回取り扱っているデータは
Baseball Savantというサイトから入手できます。

Baseball Savant


画面の「Pitchers」に名前を入力して「Search」
結果画面の右側にある
「Download Data as Comma Separated Values File」を
クリックするとCSVがダウンロードできます。




データの読み込み

Google Colabでデータを見る際は
事前にダウンロードしたデータを
ファイル置き場にアップロードしておいて下さい。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv('savant_data.csv')
print(df.shape)
df.head()




ピッチャー大谷選手のスタッツ


全球種
name_data=[
  ['Sweeper','スィーパー','スライダーよりも横曲がりの大きいボール'],
  ['4-Seam Fastball','直球','直線的な豪速球(縫い目:シームが4回見える)'],
  ['Cutter','カット・ファスト・ボール','直球とほぼ同じ球速で小さく鋭く変化するボール'],
  ['Split-Finger','フォーク','打者の近くで落下する変化球'],
  ['Sinker','シンカー','投手の利き腕方向に曲がりながら落ちる球種'],
  ['Curveball','カーブ','比較的遅い球速で大きく曲がる球'],
  ['Slider','スライダー','ブレーキがかかった大きく曲がる球'],
  ]
name_df = pd.DataFrame(name_data,columns=["pitch_name","球種名","説明"])
pitch_df = pd.DataFrame(df.pitch_name.value_counts()).reset_index()
pd.merge(pitch_df,name_df,on='pitch_name', how='left')


投球結果
stats_df = pd.DataFrame(df.description.value_counts()).reset_index()
stats_df


ストライクの投球
plt.figure(figsize=(10, 6))
x = df['plate_x']
z = df['plate_z']
d = df['description']
cs = ['blue' if 'strike' in c else 'grey' for c in d]
plt.scatter(x, z, c=cs)
plt.xlim(-10,10)
plt.ylim(-2, 6)
plt.show()
download-2





球種による変化量
plt.figure(figsize=(10, 6))
pitchtypes = ['Sweeper', '4-Seam Fastball', 'Cutter', 
'Split-Finger', 'Sinker', 'Curveball', 'Slider'] colors = ['red', 'brown', 'orange', 'aqua', 'olive', 'magenta', 'lime'] s = set() for index, row in df.iterrows(): x = row['pfx_x'] * (-30.48) # センチ換算(逆方向) z = row['pfx_z'] * ( 30.48) # センチ換算 n = pitchtypes.index(row['pitch_name']) if n not in s: s.add(n) plt.scatter(x, z, color=colors[n], label=row['pitch_name']) else: plt.scatter(x, z, color=colors[n]) plt.legend(loc="upper left", fontsize=12) plt.xlim(-100,100) plt.ylim(-60, 60) plt.show()
download





この様な感じで
投球データを色々な角度から
分析する事ができるデータになっています。

今回紹介した以外にも
たくさんのデータが存在するので
色々遊ぶ事ができると思います。

あそびたい方は
是非データをダウンロードして
こねくりまわしてみて下さい。

それでは。

このページのトップヘ