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

#はじめに
![keiba_banushi.png](https://qiita-image-store.s3.amazonaws.com/0/104983/33c7ad14-0cf2-0d18-2f6b-ce7f189e5425.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](https://jra-van.jp/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         | ルガールカルム     | ロードカナロア     | サンデースマイル２   | 207           |
| 1749         | ウイッチクイーン   | キングヘイロー     | ウイッチトウショウ   | 1243          |
| 1568         | フォークテイル     | ロードカナロア     | フォルクローレ       | 373           |
| 1528         | パーソナルビリーフ | ヴィクトワールピサ | パーソナルレジェンド | 600           |

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

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

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

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

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

なおランダムフォレストでは、特徴量の重要度を算出できますので、グラフ化してみました。
![feature.png](https://qiita-image-store.s3.amazonaws.com/0/104983/573a186c-bc19-34d0-f6ac-93278bf58a7e.png)

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

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

```python:
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: