6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Python✗Webスクレイピング】プロ野球選手データをもとにHR数を予測する

Last updated at Posted at 2022-07-04

はじめに

2022年プロ野球が開幕して早3ヶ月。
シーズン日程も折り返しましたが、
みなさま、贔屓球団の調子はいかがでしょうか?

新体制に希望を抱いて迎えた我が贔屓球団は
セ・リーグ最下位です!!!(2022/7/4現在)

そんな貧打に喘ぐ中日ドラゴンズの
「ホームラン数予測」をやってみました。

プロ野球ファン歴は19年ですが、機械学習経験は4ヶ月の初心者ですので、
どうか温かい目でご覧いただけると幸いでございます。

また、気になる点がありましたら、
ご指摘いただけると明日への励みになりますので、
ぜひコメントお待ちしております!

【データ】

プロ野球データFreak様のサイト

【ゴール】
「2021年のプロ野球選手データを使って、中日ドラゴンズの選手のホームラン数を予測する」
【タスク】
・中日ドラゴンズ以外の11球団のデータを使って機械学習

・出場試合数やヒット数などの変数から「ホームラン数」の傾向を学習させる
・学習したモデルを用い、中日ドラゴンズ選手データから「ホームラン数」を予測

【環境】
・macOS

・Python 3.9.7
・JupyterLab

ライブラリ

最初にまとめて記載する派なので、
こちらに必要ライブラリのインポートをまとめます。

qiita.py
import urllib  #HTMLにアクセス&取得
from bs4 import BeautifulSoup #HTMLからデータ抽出
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression as LR
from sklearn.metrics import mean_squared_error as MSE
import matplotlib.pyplot as plt

Webスクレイピング

※Webスクレイピングには著作権侵害等の恐れがあるため、
 規約などを確認して使用するようにしましょう。

参照記事:Webスクレイピングの注意事項一覧

qiita.py
url = 'https://baseball-data.com/21/stats/hitter-all/hr-1.html'
html = urllib.request.urlopen(url)
soup = BeautifulSoup(html, 'html.parser')

table = soup.find_all('table')[0] # HTMLから表部分を全て取得
rows = table.find_all('tr') # 表から行データを取得

# データ格納
head_data = []
player_data = []

for i, row in enumerate(rows):
    if i == 0: # 1行目
        for headerValue in row.find_all('th'):
            head_data.append(headerValue.get_text())
    elif (i > 0) and (i + 1 < len(rows)): # 2行目〜最終行未満
        player_row = []
        for playerValue in row.find_all('td'):
            player_row.append(playerValue.get_text())
        player_data.append(player_row)

# データフレーム型に変換
df = pd.DataFrame(data = player_data, columns = head_data)

# CSV出力
df.to_csv('playerdata.csv', header=True, index=False)

データ分析

今回は、目的変数「本塁打」との相関係数が0.7以上
「打席数」「打数」「安打」「打点」「四球」「三振」を予測に使う説明変数としました。

qiita.py
# CSV読み込み
df = pd.read_csv('playerdata.csv')

# 中日以外の11球団データを抽出
df11 = df[df['チーム'] != '中日']
# 中日のデータを抽出
df_d = df[df['チーム'] == '中日']

# 本塁打との相関係数
df_homerun = pd.DataFrame(df11.corr()['本塁打'])

# 可視化(相関高い長打率)
plt.scatter(x = df11['長打率'], y = df11['本塁打'])
plt.show()

# 可視化(相関低い盗塁)
plt.scatter(x = df11['盗塁'], y = df11['本塁打'])
plt.show()

# 相関係数が0.7以上の項目を抽出
x_columns = df_homerun[df_homerun['本塁打'] >= 0.7].index.tolist()
# 目的変数を除外
x_columns.remove('本塁打')

# 説明変数と目的変数を定義
X = df11[x_columns]
y = df11['本塁打']

モデル学習・評価

今回はシンプルに線形回帰モデルで学習していきます。

qiita.py
# データ分割
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state = 1)

# モデルの学習
lr = LR()
lr.fit(X_train, y_train)

# 予測値の算出
y_pred_test = lr.predict(X_test)
y_pred_test

# MSEの算出
mse_test = MSE(y_test, y_pred_test)
mse_test

予測

さあ、いよいよ予測です!

qiita.py
# 中日データ(説明変数)
x_d = df_d[x_columns]
# 予測本塁打数の算出
y_pred_d = lr.predict(x_d)
# 予測本塁打列の追加
df_d['予測本塁打'] = np.round(y_pred_d).astype(np.int32)
# 予測値と実測値との比較
print(df_d[['選手名', '本塁打', '予測本塁打']])
選手名 本塁打 予測本塁打
ビシエド 17 17
木下拓哉 11 11
福田永将 8 7
高橋周平 5 8
阿部寿樹 5 3
堂上直倫 5 6
福留孝介 4 6
京田陽太 3 1
渡辺勝 2 2
A.マルティネス 2 2
以下略

数本の誤差はありますが、概ね正確に予測できました。
それにしてもチーム本塁打数少な、、、(パァン)

おまけ

今回のコード全文を記しておきます。

qiita.py
# ライブラリ
import urllib  #HTMLにアクセス&取得
from bs4 import BeautifulSoup #HTMLからデータ抽出
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression as LR
from sklearn.metrics import mean_squared_error as MSE
import matplotlib.pyplot as plt

# スクレイピング
url = 'https://baseball-data.com/21/stats/hitter-all/hr-1.html'
html = urllib.request.urlopen(url)
soup = BeautifulSoup(html, 'html.parser')

table = soup.find_all('table')[0] # HTMLから表部分を全て取得
rows = table.find_all('tr') # 表から行データを取得

# データ格納
head_data = []
player_data = []

for i, row in enumerate(rows):
    if i == 0: # 1行目
        for headerValue in row.find_all('th'):
            head_data.append(headerValue.get_text())
    elif (i > 0) and (i + 1 < len(rows)): # 2行目〜最終行未満
        player_row = []
        for playerValue in row.find_all('td'):
            player_row.append(playerValue.get_text())
        player_data.append(player_row)

# データフレーム型に変換
df = pd.DataFrame(data = player_data, columns = head_data)

# CSV出力
df.to_csv('playerdata.csv', header=True, index=False)

# CSV読み込み
df = pd.read_csv('playerdata.csv')

# 中日以外の11球団データを抽出
df11 = df[df['チーム'] != '中日']
# 中日のデータを抽出
df_d = df[df['チーム'] == '中日']

# 本塁打との相関係数
df_homerun = pd.DataFrame(df11.corr()['本塁打'])

# 可視化(相関高い長打率)
plt.scatter(x = df11['長打率'], y = df11['本塁打'])
plt.show()

# 可視化(相関低い盗塁)
plt.scatter(x = df11['盗塁'], y = df11['本塁打'])
plt.show()

# 相関係数が0.7以上の項目を抽出
x_columns = df_homerun[df_homerun['本塁打'] >= 0.7].index.tolist()
# 目的変数を除外
x_columns.remove('本塁打')

# 説明変数と目的変数を定義
X = df11[x_columns]
y = df11['本塁打']

# モデルの作成・評価
# データ分割
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state = 1)

# モデルの学習
lr = LR()
lr.fit(X_train, y_train)

# 予測値の算出
y_pred_test = lr.predict(X_test)
y_pred_test

# MSEの算出
mse_test = MSE(y_test, y_pred_test)
mse_test

# 予測
# 中日データ(説明変数)
x_d = df_d[x_columns]
# 予測本塁打数の算出
y_pred_d = lr.predict(x_d)
# 予測本塁打列の追加
df_d['予測本塁打'] = np.round(y_pred_d).astype(np.int32)
# 予測値と実測値との比較
print(df_d[['選手名', '本塁打', '予測本塁打']])
6
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?