2
6

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 3 years have passed since last update.

【第8回】Python で競馬予想してみる ~ 今までのおさらい ~

Last updated at Posted at 2020-12-15

#競馬予想のおさらい
思い立って、コードを書き始めた python での競馬予想
今は、競馬予想をしてますが、一口馬主の募集馬の※※を・・・・だったり、株式やFXの※※を・・・・だったり
試したいことはたくさんあるのだ

ここでは、いままでのおさらいを備忘録で書いておきます
また、コードを手直ししているときに判明した問題点と その before after を試してみます

ところどころ、コードを載せてますが素人なのでご勘弁を
直した方が良いところがあれば、ご教授お願いします

#開発環境
Windows10
Jupyter Notebook

#データの取得
TARGET frontier JVのレース検索で、2000年からの全レースデータを検索
馬データおよび前走データも読込み、全データを CSV で出力し使用

#データの前処理

  • 細かな修正
    • 斤量の減量マークを外す
    • 競争除外馬の着差'----'や着順が '0' になっているのをエラー値に変換
    • 配当の欠損値を 0 に変換
    • 血統の欠損値を '不明' に変換
  • カテゴリ変数
    • 場所、芝・ダ、馬場状態、性別、父タイプ名、母父タイプ名・・・などをカテゴリ化して、ダミー変数化
  • 予想しないレース・馬データ削除
    • 予想するレースが障害レースの場合、削除
    • 予想するレースが新馬戦および初出走馬(前走レースデータがない馬)のデータ削除
  • 過去レースは5レース分連結
  • 目的変数:着順が3着以内かどうかのカラムを追加
  • トレーニングデータと検証データは、時系列で分割。最近の 3割を検証データに
  • 不均衡データ修正:アンダーサンプリング False,2 True,1 の割合に

#モデルの学習・評価
LightGBM 使用
学習結果・評価結果は以下の通り

Out
train  roc_auc_score = 0.7891163706003093
eval  roc_auc_score = 0.7758619662995246

image.png
image.png

#モデルの運用
学習結果に基づいて、閾値を変化させたときの回収率を計算すると下のグラフの通り
購入数が、購入対象レース数の 1/3 以下になった時にシミュレーションストップしてるので、購入数が極端に少ないデータは除外
複勝はいい感じです
image.png

#課題

  • 学習データを精査してみると、競争除外馬も予測対象になっていることが判明。どの程度悪影響があるか不明だが修正したほうがよさそう
  • 未出走馬を削除しているため、1~3着馬がいないレースでも予測していることが判明。レース毎に未出走馬の割合や数に応じて、予測レースから外した方がよさそう。
  • これが一番問題: 今のモデルは未勝利場もG1馬も同じ土俵で学習・予測して結果で回収率をシミュレーションしている。未確認だが、クラスの低いレースでは、予測結果の確率の高い馬の割合が低く、G1などのクラスの高いレースでは予測結果の確立が高くなっている馬ばかりと推測。特徴量の修正などで対応するか? 馬券の買い方で対応するか? 

とりあえず投稿するが、対策結果は順次 追記 する予定:wave:

#競争除外馬と未出走馬の3頭以上出走レースを削除
###学習・検証データから競争除外馬を削除

build.jpynd
    df_last = df_last.dropna(subset = ['前走レースID(新)']) # 前走レースID がないデータ削除

###3頭以上未出走馬が出走するレースを削除

3頭以上未出走馬が出走するレースを削除

build.jpynd
# 未出走馬が一定数以上のレースは除外
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順

build.jpynd
# 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)

回収率の計算に使うテーブルはこんな感じ
スクリーンショット 2020-12-15 09.17.51.png

##学習・検証結果
####競争除外馬と未出走馬が2頭以上のレースを削除 回収率シミュレーションは変更なし
Auc はほとんど変化なし

Out
train  roc_auc_score = 0.7891023931796946
eval  roc_auc_score = 0.77596700811738

##回収率シミュレーション
今まで通り Proba の値を基に閾値を動かして検証
image.png

Out
WIN_回収率 81.82452193475815
PLACE_回収率 88.57142857142857

ちょっとは、良化すると思ったけどほとんど変化なしなのだ

####競争除外馬と未出走馬が2頭以上のレースを削除 回収率を Probaをレース毎に標準偏差し正規化したものでシミュレーション
image.png

Out
WIN_max回収率 81.76498653852708
PLACE_max回収率 85.47403863193337

####競争除外馬と未出走馬が 2頭以上のレースを削除 回収率をレース毎に Proba 上位 n頭を購入
n を 1~3頭に変化させても、単勝・複勝の回収率は 80-81%程度

色々試したけど、大して良化しないのだ

今回はここまで:wave:

2
6
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
2
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?