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


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

新型コロナウィルスの感染者増加が止まりません。

このまま行ったらどうなるのかを
データから予測してみました。


解説動画はこちら



さて
今回は予測モデルを使って未来の予測を行います。

用いるライブラリとしては
pandas
matplotlib
fbprophet
になります。

一番初めにデータを読み込んでみましょう。

pandasでデータの提供元(奥村研究所)から
ダウンロードしてデータ化します。
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib inline

df = pd.read_csv("https://oku.edu.mie-u.ac.jp/~okumura/python/data/COVID-19.csv",
                 index_col='Date', parse_dates=['Date'])

これでデータフレームにデータが読み込まれます。

データは4列あるようです。

まずは感染者数の推移を見てみましょう。
df.iloc[0:,0:2].plot(figsize=(16,9))
plt.show()
download

直近での推移が爆裂していますね。
増加率も見てみましょう。
pandasの関数 pct_changeで増加率を算出できます。

df['diff_Global Confirmed'] = df['Global Confirmed'].pct_change()
df['diff_Global Deaths'] = df['Global Deaths'].pct_change()
plt.figure(figsize=(16,9))
plt.bar(df.index,df['diff_Global Confirmed'],alpha=0.3,label="Confirmed")
plt.bar(df.index,df['diff_Global Deaths'],alpha=0.3,label="Deaths")
plt.legend()
plt.show()
download-1

1月末ごろから
中国での大流行があったことが
みて取れます。


中国を抜かした世界の推移を見てみましょう。
別の列を用意してあげます。
df['Confirmed2']=df['Global Confirmed'] - df['China Confirmed']
df['Deaths2']=df['Global Deaths'] - df['China Deaths']
df.iloc[0:,6:8].plot(figsize=(16,9))
plt.show()

download-2

中国以外は3月の頭くらいから
急激に増えているようですね。

増加率も見てみると

df['diff_Confirmed2'] = df['Confirmed2'].pct_change()
df['diff_Deaths2'] = df['Deaths2'].pct_change()
plt.figure(figsize=(16,9))
plt.bar(df.index,df['diff_Confirmed2'],alpha=0.3,label="Confirmed")
plt.bar(df.index,df['diff_Deaths2'],alpha=0.3,label="Deaths")
plt.legend()
plt.show()
download

2月の手前に急激に増えた日がありますね。
ここら辺が起点になっているようです。

次に死亡率です。
df['Death Rate'] = df['Deaths2'] / df['Confirmed2']
plt.figure(figsize=(16,9))
plt.bar(df.index,df['Death Rate'],alpha=0.8,label="Death Rate")
plt.legend()
plt.show()
download-1

直近では5%近くなっていてどんどん上がってきてもいます。

このまま増え続けるとどうなるのか
感染者数の推移を予測してみます。
facebookのprophetを用います。

これはインストールをしないと使えないので
お気をつけください。
from fbprophet import Prophet
import warnings

warnings.simplefilter('ignore',)
model_df = df[['Confirmed2']]
model_df = model_df.reset_index()
model_df = model_df.rename(columns={'Date':'ds','Confirmed2':'y'})
model_df.head()
予測用のデータは
ds,yの2列を用意してあげます。
dsは日付、yは感染者数です。


ds
y
02020-01-204
12020-01-215
22020-01-2310
32020-01-2416
42020-01-2523


モデルを作ります。
model = Prophet()
model.fit(model_df)

最後に予測です。
予測したい日数分の空のデータフレームを用意します。

空のデータフレームを使って
学習したモデルで予測します。
future_df = model.make_future_dataframe(600)
forecast_df = model.predict(future_df)
model.plot(forecast_df)

可視化もできます。
結果は
スクリーンショット 2020-03-29 17.17.04

このままの推移が続くと
今年の終わりには600万人
来年の6月にはその倍くらいにはなっています。

すでに対策を始めていますので
ここまでにはならないと思いたいですが
不要不急の用事がない限りは
あまり出歩かない方が良いでしょうね。

死亡率も5%近くということで
20人のうち一人亡くなると考えると
無視できない確率かなと
個人的には思います。

リモートワークが続いて
ずっと家にいるわけですが

早く
収束してくれることを
願うばかりであります。


はいどうもこんばんは乙pyです。

今日はニコニコ動画さんの
有料会員の推移が気になってしまったので
有料会員数の予測をしてみました。

解説動画はこちら


まずニコニコ動画さんの
最近の有料会員数ですが
決算などで発表されているので
そこからデータ化しました。
年(期) 会員数(万人)
2015-09 253
2015-12 254
2016-03 256
2016-06 256
2016-09 256
2016-12 252
2017-03 243
2017-06 236
2017-09 228
2017-12 214
2018-03 207
2018-06 200
2018-09 194
2018-12 188


さてこれをPythonを使って予測していきます。
最初に必要なライブラリの読み込みです。
from fbprophet import Prophet
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

次の会員数をデータ化します。
データはPandasのデータフレームにします。
df = pd.DataFrame({
    'ds': pd.to_datetime([
        "2015-09-01","2015-12-01","2016-03-01","2016-06-01",
        "2016-09-01","2016-12-01","2017-03-01","2017-06-01",
        "2017-09-01","2017-12-01","2018-03-01","2018-06-01",
        "2018-09-01","2018-12-01"])
    ,'y': [253,254,256,256,256,252,243,236,228,214,207,200,194,188]
} , index=pd.to_datetime([
    "2015-09-01","2015-12-01","2016-03-01","2016-06-01",
    "2016-09-01","2016-12-01","2017-03-01","2017-06-01",
    "2017-09-01","2017-12-01","2018-03-01","2018-06-01",
    "2018-09-01","2018-12-01"
]))
これでデータフレームの完成です。

一旦可視化をして推移を見てみましょう。
plt.figure(figsize=(12, 6))
plt.scatter(df.index, df['y'])
plt.xticks(rotation=70)
plt.show()
nico01

このような会員数の推移ですね。
2015年あたりをピークに
そこからは下降路線をたどっています。

この推移から未来を予測します。

予測にはFacebookの
Propfetというライブラリを用います。

モデルの呼び出しも簡単です。
#モデルの呼び出し
model = Prophet()
学習も1行で終わります。
#学習
model.fit(df)
学習させるにはコツが必要で
時間に該当するものを ts というカラムでTimestampにしておく
数値は y というカラムでデータフレームに定義しておく
必要があります。

ファイルを読み込んでデータフレームを作った際は
カラム名やカラムのデータ型を
Propfet用に合わせる必要があります。

次に予測です。

予測は未来3000日分としました。
このライブラリは日で計算されるようで
引数 periods に日数を入れてあげて
model.make_future_dataframe()という関数を
呼んであげます。

そうすると予測用のデータフレームが作られます。

最後にmodel.predict()で作ったデータフレームで
予測結果のデータフレームを作ります。
#予測
future = model.make_future_dataframe(periods=3000)
forecast = model.predict(future)
たったこれだけで未来予測が終わります。

使える変数は日付と数値だけと
変数が少ないので
簡易な予測にはもってこいのライブラリです。


最後に予測の結果を可視化しましょう。
# 可視化
model.plot(forecast) plt.show()
nico02

日での予測の結果はかなり上下動が激しい感じなので
周期性の方を見てみます。

model.plot_components(forecast)
plt.show()
nico03

はい
これだと綺麗に予測の結果が出ますね。

このままの下降ラインをたどると
2026年には有料会員数が0になるという結果がでました。

下振れが激しければ2024年でも
有料会員数が0になる未来もあり得るという予想結果でした。

あくまでも現状の数値を使っての予測のため
そのままの下降線をたどるということは
少ないかもしれませんが

施策次第では
V字回復もあり得るシナリオではあるので
これからのニコニコ動画さんの
巻き返しに注目したいところです。

頑張れニコ動!!!

このページのトップヘ