#競馬予想のおさらい
思い立って、コードを書き始めた python での競馬予想
今は、競馬予想をしてますが、一口馬主の募集馬の※※を・・・・だったり、株式やFXの※※を・・・・だったり
試したいことはたくさんあるのだ
ここでは、いままでのおさらいを備忘録で書いておきます
また、コードを手直ししているときに判明した問題点と その before after を試してみます
ところどころ、コードを載せてますが素人なのでご勘弁を
直した方が良いところがあれば、ご教授お願いします
#開発環境
Windows10
Jupyter Notebook
#データの取得
TARGET frontier JVのレース検索で、2000年からの全レースデータを検索
馬データおよび前走データも読込み、全データを CSV で出力し使用
#データの前処理
- 細かな修正
- 斤量の減量マークを外す
- 競争除外馬の着差'----'や着順が '0' になっているのをエラー値に変換
- 配当の欠損値を 0 に変換
- 血統の欠損値を '不明' に変換
- カテゴリ変数
- 場所、芝・ダ、馬場状態、性別、父タイプ名、母父タイプ名・・・などをカテゴリ化して、ダミー変数化
- 予想しないレース・馬データ削除
- 予想するレースが障害レースの場合、削除
- 予想するレースが新馬戦および初出走馬(前走レースデータがない馬)のデータ削除
- 過去レースは5レース分連結
- 目的変数:着順が3着以内かどうかのカラムを追加
- トレーニングデータと検証データは、時系列で分割。最近の 3割を検証データに
- 不均衡データ修正:アンダーサンプリング False,2 True,1 の割合に
#モデルの学習・評価
LightGBM 使用
学習結果・評価結果は以下の通り
train roc_auc_score = 0.7891163706003093
eval roc_auc_score = 0.7758619662995246
#モデルの運用
学習結果に基づいて、閾値を変化させたときの回収率を計算すると下のグラフの通り
購入数が、購入対象レース数の 1/3 以下になった時にシミュレーションストップしてるので、購入数が極端に少ないデータは除外
複勝はいい感じです
#課題
- 学習データを精査してみると、競争除外馬も予測対象になっていることが判明。どの程度悪影響があるか不明だが修正したほうがよさそう
- 未出走馬を削除しているため、1~3着馬がいないレースでも予測していることが判明。レース毎に未出走馬の割合や数に応じて、予測レースから外した方がよさそう。
- これが一番問題: 今のモデルは未勝利場もG1馬も同じ土俵で学習・予測して結果で回収率をシミュレーションしている。未確認だが、クラスの低いレースでは、予測結果の確率の高い馬の割合が低く、G1などのクラスの高いレースでは予測結果の確立が高くなっている馬ばかりと推測。特徴量の修正などで対応するか? 馬券の買い方で対応するか?
とりあえず投稿するが、対策結果は順次 追記 する予定
#競争除外馬と未出走馬の3頭以上出走レースを削除
###学習・検証データから競争除外馬を削除
df_last = df_last.dropna(subset = ['前走レースID(新)']) # 前走レースID がないデータ削除
###3頭以上未出走馬が出走するレースを削除
3頭以上未出走馬が出走するレースを削除
# 未出走馬が一定数以上のレースは除外
rouped = df_return_table.groupby('レースID(新/馬番無)')
num_horse = lambda x : x.count() - x.mean()
df_return_table['データ減'] = grouped['出走頭数'].transform(num_horse)
df_return_table = df_return_table[~(df_return_table['データ減'] <= -3)]
###proba の値を標準偏差化して 0~1に収まるように修正
レース内で Proba を標準偏差に直し(下表のProba_std
) その数値を 0~1 に収まるように修正してみた(下表のProba_std01
)
ついでにレース内で Proba を順位付けした列を追加(下表のProba順
)
# proba 順位付け
df_return_table['Proba順'] = df_return_table.groupby('レースID(新/馬番無)').rank('dense', ascending = False)['Proba']
# proba のレース内標準偏差化
std_scaler = lambda x: (x-x.mean()) / x.std()
df_return_table['Proba_std'] = df_return_table.groupby('レースID(新/馬番無)')['Proba'].transform(std_scaler)
std01_scaler = lambda x: (x - x.min()) / (x.max() - x.min())
df_return_table['Proba_std01'] = df_return_table['Proba_std'].transform(std01_scaler)
##学習・検証結果
####競争除外馬と未出走馬が2頭以上のレースを削除 回収率シミュレーションは変更なし
Auc はほとんど変化なし
train roc_auc_score = 0.7891023931796946
eval roc_auc_score = 0.77596700811738
##回収率シミュレーション
今まで通り Proba の値を基に閾値を動かして検証
WIN_回収率 81.82452193475815
PLACE_回収率 88.57142857142857
ちょっとは、良化すると思ったけどほとんど変化なしなのだ
####競争除外馬と未出走馬が2頭以上のレースを削除 回収率を Probaをレース毎に標準偏差し正規化したものでシミュレーション
WIN_max回収率 81.76498653852708
PLACE_max回収率 85.47403863193337
####競争除外馬と未出走馬が 2頭以上のレースを削除 回収率をレース毎に Proba 上位 n頭を購入
n を 1~3頭に変化させても、単勝・複勝の回収率は 80-81%程度
色々試したけど、大して良化しないのだ
今回はここまで