#はじめに
競艇のルール,予想の仕方など私自身何もその辺の知識がないことをご了承ください.
それらすべてをデータ分析,機械学習に任せることによって知識のない人間でも利益をプラスにできるのではないか,そういう魂胆でこの記事を書いています.
環境
MacOS BigSur 11.4
Jupyter Notebook(Colaboratoryでも可)
データについて
競艇公式ホームページ BOAT RACEからスクレイピングによってデータを取得しました.
会場:住之江会場
データ数:43560件
期間:2019/01 ~ 2020/04
今回の目的
競艇はスタート位置でほぼ決まる.みたいな話を聞いたことがあります.
データ分析によってこれが本当なのかを調べるのが今回の目標です.
本編
ライブラリのimport
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from itertools import product
## 前処理
データはsuminoe_40000_program.csv
とsuminoe_40000_answer.csv
に格納されているため,PandasによってDataFrame形式でデータを読み込みます.
説明変数
names = ["Place", "Number", "Name", "Age", "Live",
"Weight", "Rank", "A_1st", "A_2nd", "B_1st",
"B_2nd", "Moter_No", "Moter_2nd", "Bote_No", "Bote_2nd"]
df_program = pd.read_csv("../../Boat-Race-Prediction/suminoe_40000_program.csv", names=names)
print(f"shape: {df_program.shape}")
df_program.head()
目的変数
df_answer = pd.read_csv("../../Boat-Race-Prediction/suminoe_40000_answer.csv", names=["idx", "target"])
print(f"shape: {df_answer.shape}")
df_answer
OneHotEncoding
説明変数を見ると,Name
, Live
, Rank
はカテゴリ変数となっていることがわかります.
名前Name
と地域Live
についてはOneHotEncodingしてしまうと ,列数がとんでもないことになってしまうため今回はデータから弾くことにします.
よってRank
をOneHorEncodingによって,A1
, A2
, B1
, B2
の列を新たに作成します.
(目的変数のidx
も必要ないため弾いています.)
df_data = df_program.merge(df_answer, how="inner", right_index=True, left_index=True)
df_data = df_data.drop(["idx", "Name", "Live"], axis=1)
df_dummies = pd.get_dummies(df_data, columns=["Rank"])
df_dummies.head()
目的変数の前処理
競艇には転覆やスタートミスによって失格判定になる場合があります.
生データではtarget
の部分がK
, F
のようなカテゴリ変数となっています.
できるだけ数値のみのデータとして扱いため,10
, 11
, 12
の値に変換してあります.
今回の分析でこれらのデータは使用しないため,np.nan
によって欠損値扱いとします.
df_dummies.target[df_dummies.target == 10] = np.nan
df_dummies.target[df_dummies.target == 11] = np.nan
df_dummies.target[df_dummies.target == 12] = np.nan
各列とtarget
の相関を見る
target
と各変数の関係を相関係数を計算することによって調べます.
わかりやすさのために,Seaboan
のヒートマップを使用しています.
# 相関係数を表示
plt.rcParams['figure.figsize'] = (20.0, 10.0)
plt.rcParams['font.family'] = 'serif'
sns.heatmap(df_data.corr(), annot=True, square=True, cmap='coolwarm');
このヒートマップから,target
とPlace
の間には,0.38
の正の相関があることがわかります.
0.38
という値は強い相関とは言えませんが,やや相関があると言えます.
なので,target
とPlace
をヒストグラムで示してみましょう.
target
とPlace
の関係をヒストグラムで可視化する
各スタート位置に対する順位をヒストグラムによって可視化してみます.
これによって位置がどのくらい順位に影響するのかがわかるはずです.
fig, ax = plt.subplots(2, 3, figsize=(15, 10))
for i, tu in zip(range(1, 7), product(range(2), range(3))):
df_place = df_dummies[df_dummies.Place == i]
ax[tu[0], tu[1]].hist(df_place.target, bins='auto')
ax[tu[0], tu[1]].title.set_text(f'Place {i}')
ax[tu[0], tu[1]].set_xlabel('Ranking')
ax[tu[0], tu[1]].set_ylabel('Count')
plt.tight_layout()
スタート位置が1
である場合に1位になる確率はとてつもなく高そうです.
スタート位置が6
に近づくにつれて1位になる確率は下がっていっていることがわかります.
では,位置に対する順位の確率を算出してみましょう.
for i in range(1, 7):
df_place = df_dummies[df_dummies.Place == i]
print(f"Place: {i}")
for j in range(1, 7):
df_target = df_place[df_place.target == j]
probability = df_target.shape[0] / df_place.shape[0] * 100
print("\t{}: {:.1f}%".format(j, probability))
Place: 1
1: 55.9%
2: 16.9%
3: 9.1%
4: 6.8%
5: 5.5%
6: 4.6%
Place: 2
1: 15.7%
2: 25.4%
3: 18.3%
4: 15.1%
5: 13.0%
6: 11.3%
Place: 3
1: 11.3%
2: 21.5%
3: 20.3%
4: 18.2%
5: 15.1%
6: 12.2%
Place: 4
1: 8.7%
2: 16.1%
3: 19.5%
4: 19.0%
5: 17.8%
6: 17.3%
Place: 5
1: 5.4%
2: 11.7%
3: 17.0%
4: 20.0%
5: 23.0%
6: 21.4%
Place: 6
1: 3.0%
2: 8.4%
3: 15.7%
4: 20.6%
5: 24.4%
6: 26.4%
スタート位置が1
であったときに1位になる確率は,55.9%
であることがわかりました!
よって,1レース6人であるため,データでは1レース6行で表現されます.なので,今回のデータは43560件であるため,
43560 / 6 = 7260
レース分のデータが存在しています.
この7260レース中のうち,55.9%
が1位になるため,スタート位置が1
である選手は7360
中4058
回1位になっていることが今回のデータからわかりました.
まとめ
絶対というわけではないですが,スタート位置だけでも順位にそれなりに影響するようです.
しかしスタート位置が2
, 3
, 4
の場合は順位にあまり差がないため,3連単などを当てるためには他の点で分析していく必要があると思います.
ここまでのデータ分析で使えるのは単勝予想くらいだと思います.