15
18

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

【競馬】2019年のダービー馬を機械学習で予測する

Posted at

#はじめに
keiba_banushi.png

みなさんはPOG(ペーパーオーナーゲーム)をご存知でしょうか?
競馬にまつわるゲームで、デビュー前の2歳馬の仮想馬主となり、1年間の獲得賞金を競います。

このゲームの必勝法はただ一つ。ダービーを勝つ馬を指名することです。
ダービーは同世代の最強の馬を決めるレースで、優勝賞金が2億円とPOG期間中に出走できるレースの中で最も高額なレースになります。
そのためPOG参加者はダービー馬を探すため、本やらネットの情報を必死にかき集め頑張るわけです:sweat_smile:

今回はそのダービー馬を機械学習で予測してみようという試みです。
既に新馬戦開始から3ヶ月が経過し、出遅れ感はありますが、意外と面白い結果が出たので公開してみることとしました。

#手法
RandomForestで回帰を行います。
目的変数には収得賞金を利用しました。
収得賞金とは競走馬のクラス分けに使う基準値で、基本的に高いほど強いという形式が成り立っているため、こちらを採用しました。

#使用言語/ライブラリ/ソフトウェア
■TARGET Frontier JV(Windows)
競馬データを自由自在に検索できる有名ソフトウェア。
CSV出力機能があるので、こちらのソフトで競馬データを生成します。

■Microsoft Excel
CSVデータの整形

■Python3
実装言語
Rでも可能かと思いますが、使い慣れているPythonを選択しました。

■sklearn(RandomForestRegressor)
ランダムフォレストの実装

■pandas
CSVのデータ化
(ダミー変数化処理や、numpy arrayへの変換など。)

■matplotlib
予測結果などのグラフ描画

#データの準備

  1. TARGET Frontier JVより競走馬のCSVを出力
  2. ExcelでCSVを開き、データ処理
  3. PandasでCSVを開き、データ処理

という順番で、ランダムフォレストで学習できるような形式にデータを整備していきます。
ExcelでのCSV整備はスマートではありませんが、私のPandas力が足りないため、このような手順を踏んでおります:sweat_smile:

#説明変数について
今回予測に利用する説明変数は以下の7つです。

  • 性別
  • 所属
  • 調教師
  • 種牡馬
  • 馬主
  • 生産牧場
  • 兄弟勝利数

##性別
せん馬、牡馬、牝馬とありますが、こちらはそのまま3カテゴリとしてダミー変数化します。
せん馬は去勢手術をした馬のことで、気性の悪い馬に対して行われるのですが、2歳の段階でせん馬になっている馬は少ないので、特別な考慮は必要無いと判断しました。
なお基本的に、牝馬より牡馬のほうが強い傾向にあります。

##所属
競走馬が所属している厩舎が東西どちらにあるか、というデータになります。
美浦が関東、栗東が関西となります。
他にも地方、外国というデータ種別がありますが、特殊なデータとなるため削除した上で、ダミー変数化しました。

##調教師
競走馬に調教をつけるのが調教師です。
ハードトレーニングでビシバシやる調教師もいれば、緩めの調教で速い時計を出さない調教師もいます。
やはり一流の調教師と言われるところには、良い馬が入ってくる確率が高くなります。
調教師は200名ほどいて全てダミー変数化すると多いため、勝利数上位の調教師に絞ってダミー変数化し、残りはその他として一括りにしています。

##種牡馬
競走馬の父親に当たる馬が種牡馬です。競走馬の場合、同じ馬が1年に100頭以上種づけする場合もあるので、ある程度種類は絞られます。ただ海外から輸入した馬などを含めると、やはり相当な種類となってしまうため、こちらも一部をダミー変数化し、残りはその他としました。

##馬主
競走馬のオーナーにあたるのが馬主です。
国内最大のせり市場であるセレクトセールでは、2億、3億という超高価格で競走馬が落札されることもあります:scream:
つまりお金持ちです。えぇ。

一番有名な個人馬主である金子真人オーナーは、名馬ディープインパクトをはじめ、ダービー4勝と異常な記録を残しています。有力なオーナーには、せり市場を通さずに直接馬が売買されることもあり(庭先取引といいます)、馬主情報は重要なファクターとなりえます。

こちらも一部をダミー変数化し、残りはその他としました。

##生産牧場
競走馬は基本的に、北海道の牧場で生産されます。
中でもノーザンファームという牧場が圧倒的な成績を残しており、1強といった感じです。

こちらも一部をダミー変数化し、残りはその他としました。

##兄弟勝利数
成績を残した馬の弟や妹は、人気になりやすく、また結果も残しやすい傾向にあります。
そのあたりは人間と同じでしょうか…?

今回は兄弟馬の中で一番勝利数が多かった馬の勝利数を、整数値(int)として与えています。
兄弟の成績を説明変数としたい場合、兄弟の収得賞金の平均を取るのが良いかと思いますが、今回はTARGETから簡単に出力できるこのデータを利用しました。

#予測
競走馬登録されている2歳馬を対象とし、予測を行います。
理由は、競走馬登録されていない馬については、調教師や馬主の情報が入っておらず欠損値になるためです。
頭数としては全体の半分程度(2748頭)になってしまいますが、ダービー目指す馬であればこの時期には登録されている確率は高いため、あまり問題にはならないかと思います。

#結果
上位1頭のみを載せてもつまらないので、今回は予測収得賞金が1500万円以上の馬を載せてみます。
ダービーは牡馬/牝馬ともに出走できるのですが、見通しを良くするため、表を分けました。

なお参考として、JRA-VAN POGの指名数の順位を掲載しています。こちらの値が小さいほど、人間の予測上位の馬となります。

##牡馬

予測収得賞金 馬名 POG指名数順位
8133 フランクリン ディープインパクト ロベルタ 16
7910 アラゴネス ゴールドアリュール アンソニカ 1713
4736 リスト ディープインパクト シルヴァースカヤ 14
3988 アルテヴェルト ハーツクライ アルテリテ 179
3774 ウーリリ ディープインパクト ウィキウィキ 22
3005 ミッキースピリット ディープインパクト フリーティングスピリット 130
2918 ヴィアロマーナ ロードカナロア ローマンエンプレス 211
2739 ブーザー マンハッタンカフェ マンドゥラ 129
2654 スマイル ダイワメジャー アシュレイリバー 519
2649 ランフォザローゼス キングカメハメハ ラストグルーヴ 41
2607 ペルソナデザイン ハーツクライ コマーサント 800
2601 ヴァンドギャルド ディープインパクト スキア 132
2163 クラージュゲリエ キングカメハメハ ジュモー 52
2117 マイネルエキサイト ブラックタイド アインライツ 1924
1930 コスモアドム スクリーンヒーロー コスモルビー 2446
1920 ミトロジー ダイワメジャー リードストーリー 359
1882 アスクフラッシュ エイシンフラッシュ レッドデセーオ 377
1840 オメガ ダイワメジャー リアリサトリス 164
1673 カントル ディープインパクト ミスアンコール 6
1671 エデリー ディープインパクト ヴァレリカ 55
1651 ソルドラード ロードカナロア ラドラーダ 2
1605 サトノソロモン ディープインパクト イルーシヴウェーヴ 18
1572 バイキングクラップ ハーツクライ マジックストーム 26
1568 ダノンチェイサー ディープインパクト サミター 8
1552 レイズアフラッグ キングカメハメハ レディシャツィ 413

##牝馬

予測収得賞金 馬名 POG指名数順位
6727 ジョイントベンチャ スクリーンヒーロー ヴィエントバイラー 2690
6697 カウディーリョ キングカメハメハ ディアデラノビア 49
5372 グランデストラーダ ハーツクライ レジェンドトレイル 238
5145 ベルクワイア ロードカナロア スカーレットベル 12
4906 ロフティネス スクリーンヒーロー フジヤマサクラ 1323
3545 ヴァンランディ キングカメハメハ ハッピーパス 69
3393 カレンソナーレ オルフェーヴル フィエラメンテ 240
3242 マンドゥーリア ハーツクライ アートプリンセス 1243
2545 ブランノワール ロードカナロア プチノワール 172
2543 ダイアナブライト ディープインパクト チェリーコレクト 205
2152 ヤンチャヒメ クロフネ ウエストコースト 1849
2021 グロリアーナ ハーツクライ ベネンシアドール 44
1765 ルガールカルム ロードカナロア サンデースマイル2 207
1749 ウイッチクイーン キングヘイロー ウイッチトウショウ 1243
1568 フォークテイル ロードカナロア フォルクローレ 373
1528 パーソナルビリーフ ヴィクトワールピサ パーソナルレジェンド 600

ということで、機械学習で予測するダービー馬は
「フランクリン」(父:ディープインパクト/母:ロベルタ)
となりました!

#考察
予測収得賞金1位のフランクリンは、ダービー3着馬アドミラブルと似たプロフィールで、近親に活躍馬がかなり多い血統です。JRA-VAN POGの指名数でも16位と上位で、かなり固めの予測をしてきたな…という感じです。

2位は牝馬のジョイントベンチャという馬でした。特に兄弟に活躍馬はおらず、父スクリーンヒーローが評価ポイントかと思います。スクリーンヒーロー産駒はモーリス、ゴールドアクターと大活躍した馬がいますが、どちらも牝系は優秀ではなく、そのあたりも共通点でしょうか。なお残念ながらこの馬は1走もせず登録抹消してしまったようなので、ダービーで見かけることはないでしょう…。

3位のアラゴネスは、ノーザン×吉田勝己×ゴールドアリュールと、G1馬ゴールドドリームと似たプロフィールで、ダート路線で活躍が見込めそうです。

4位(牝馬2位)のカウディーリョは、母ディアデラノビア、姉ディアデラマドレ共に重賞戦線で活躍した血統馬で、既に新馬戦に出走し快勝しています。ダービーとまではいかなくても、桜花賞・オークスは十分狙えそうです。

なおランダムフォレストでは、特徴量の重要度を算出できますので、グラフ化してみました。
feature.png

兄弟勝利数の棒が顕著に伸びていますが、これはこの変数だけ整数値であるのが影響しているかと思います(ほかは0,1の論理値)。
またスクリーンヒーロー産駒の重要度が大きいですが、何故でしょうか…?確かに大活躍した馬はいますが、種牡馬ではディープインパクトが一番重要度が大きいと思っていたので、これは意外な結果でした。

#コード
今回の予測に使用したコードです。

import pandas as pd
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestRegressor

font = {'family':'IPAexGothic'}

horse_table = pd.read_csv('C:/TFJV/TXT/3_7yo.csv',encoding='SHIFT-JIS')
sex_table = pd.get_dummies(horse_table['性別'])
syozoku_table = pd.get_dummies(horse_table['所属'])
trainer_table = pd.get_dummies(horse_table['調教師名'])
sire_table = pd.get_dummies(horse_table['種牡馬名'])
breeder_table = pd.get_dummies(horse_table['生産者名'])
owner_table = pd.get_dummies(horse_table['馬主名'])
analyze_table = pd.concat([sex_table, syozoku_table, trainer_table, sire_table, breeder_table, owner_table,  horse_table.loc[:,['兄弟勝利数(上位1頭)','収得賞金']]], axis=1)

X = analyze_table.iloc[:23795, :-1].values
y = analyze_table.loc[:23794, '収得賞金'].values
horse_name = horse_table.loc[:23794, '馬名'].values

# 学習データを使ってモデルを構築する
r_forest = RandomForestRegressor(
            n_estimators=500,
            criterion='mse',
            n_jobs=-4
)
r_forest.fit(X, y)

# 特徴量の重要度の表示
fti = r_forest.feature_importances_
feature_list = []
for i, column_name in enumerate(analyze_table.iloc[:, :-1]):
    feature_list.append([column_name, fti[i]])

feature_list = sorted(feature_list, key=lambda x: x[1], reverse=True)
feature_key = []
feature_value = []

for i in range(20):
    feature_key.append(feature_list[i][0])
    feature_value.append(feature_list[i][1])

feature_key.reverse()
feature_value.reverse()

plt.figure()
plt.barh(range(20),feature_value)
plt.yticks(range(20), feature_key,**font)
plt.tight_layout()
plt.show()

#2歳馬の予測
X_2yo = analyze_table.iloc[23795:, :-1].values
y_2yo = analyze_table.ix[23795:26522, '収得賞金'].values
horse_name_2yo = horse_table.ix[23795:26522, '馬名'].values

y_2yo_pred = r_forest.predict(X_2yo)

for i, price_predin enumerate(y_2yo_pred.tolist()):
    if int(price_pred) > 1500:
        print(horse_name_2yo[i],int(price_pred),y_2yo[i])

#おわりに
今回はかなりアバウトなデータ整備でしたが、人間の予測と遠くない、もっともらしい結果を出すことができました。
まだまだ予測モデルを詰める余地あると思うので、引き続き研究したいと思います。

また私は機械学習の知識はまだまだですが、競馬については詳しいと思いますので、
プロKagglerの方とタッグを組めれば、かなり魅力的な予測モデルが作れるのではないかと思いました。
もし興味のある方がいらっしゃいましたらご連絡ください:innocent:

15
18
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
15
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?