修士論文の執筆が忙しくだいぶ間が空いてしまいましたが、第4回の記事では各ソースから収集してきたデータを整理する方法について書いていきます。
まずは馬柱をつくる
競馬の1レースが持つ情報には、競馬場や距離などのレースに関する情報、また出走する各馬の馬番、負担重量、騎手、プロフィール、過去戦績などがあります。 さらに各戦績はレース情報、負担重量、騎手、着順・タイムなどの結果を持っており、非常に複雑です。
機械学習アルゴリズムの入力となる特徴量を作成するためには、それら複雑なデータを整形していく必要があります。 例えば、分+秒形式のタイムを秒に変換したり、走破タイムをレース内で標準化したり、過去の着順から勝率を計算するなどです。 それらの泥臭いデータ整形をスクリプトやSQLのクエリとしてベタ書きをしていくと、保守性が失われ、後々手の施しようがないプログラムになってしまいます(経験談)。
そこでAlphaImpactでは収集してきたデータから『馬柱』を作ることでデータの管理や生成の効率化を実現しています。
馬柱とは競馬新聞1に載っているレースに必要な情報がまとまっている表のことです。
『馬柱』をつくるといっても実際に競馬新聞をつくるのではなく、馬柱を模倣したデータ構造をプログラムで再現します。
煩雑な生データを馬柱で包むことによって、予測やデータ成形のインターフェースを統一することができるので、プログラムの見通しも良くなります。
例えばAlphaImpactでは、レースの予測を出すときは predict(horse_table) 、特徴ベクトルを生成する場合はcreate_data(horse_table) のように簡潔なコードで記述できています。
馬柱を要素に分解
馬柱は以下の図のように要素を分解できます。
次に各データ種別ごとのデータの例を紹介します。
開催情報
レースが行われる開催における情報です。
| データ名 | 備考 |
|---|---|
| 競馬場 | 函館・札幌・福島・新潟・東京・中山・中京・京都・阪神・小倉 |
| 年月日 | |
| 開催回 | 第N回 |
| 開催日 | N日目 |
| 曜日 | |
| 天候 | |
| 馬場状態 | 良・稍重・重・不良 |
番組情報
レースに関する情報です。
| データ名 | 備考 |
|---|---|
| レース名 | 例)日本ダービー |
| トラック | 芝・ダート・障害 |
| コース距離 | |
| 右左 | コーナーの回り向き |
| 内外 | コースの内外 |
| クラス条件 | 新馬・未勝利・500万下・1000万下・1600万下・OP |
| グレード | GI・GII・GIII |
| 本賞金 | 1着~5着まで |
| 年齢条件 | 2歳・3歳・3歳以上・4歳以上 |
| 競走記号 | 牡馬・牝馬限定など |
| 出走頭数 |
払戻情報
レースの払戻に関する情報です。
| データ名 | 備考 |
|---|---|
| 払戻金 | 単勝・複勝・枠連・馬連・ワイド・馬単・3連複・3連単・Win5 |
| 的中組番 | 単勝・複勝・枠連・馬連・ワイド・馬単・3連複・3連単・Win5 |
馬属性情報
馬固有のプロフィールです。
| データ名 | 備考 |
|---|---|
| 馬名 | 例) ディープインパクト |
| 生年月日 | |
| 性別 | |
| 毛色 | |
| 血統 | 父・母・父母など |
| 馬主 | |
| 生産者 | |
| 所属厩舎 |
馬毎レース情報
出走レースにおける出走馬の状態に関する情報です。
| データ名 | 備考 |
|---|---|
| 馬番 | |
| 枠番 | |
| 負担重量 | |
| 騎手名 | 例)横山典弘 |
| 馬体重 | |
| 馬体重増減 |
過去戦績
出走時点における過去の出走レースの条件や結果に関する情報です。
| データ名 | 備考 |
|---|---|
| 競馬場 | 函館・札幌・福島・新潟・東京・中山・中京・京都・阪神・小倉 |
| 年月日 | |
| 天候 | |
| 馬場状態 | 良・稍重・重・不良 |
| レース名 | 例)日本ダービー |
| トラック | 芝・ダート・障害 |
| コース距離 | |
| 右左 | コーナーの回り向き |
| 内外 | コースの内外 |
| クラス条件 | 新馬・未勝利・500万下・1000万下・1600万下・OP |
| グレード | GI・GII・GIII |
| 本賞金 | 1着~5着まで |
| 年齢条件 | 2歳・3歳・3歳以上・4歳以上 |
| 競走記号 | 牡馬・牝馬限定など |
| 出走頭数 | |
| 馬番 | |
| 負担重量 | |
| 騎手名 | 例)横山典弘 |
| 馬体重 | |
| 馬体重増減 | |
| 着順 | |
| 走破タイム | |
| 前後3Fタイム | 前半・後半600mのタイム |
| コーナー通過順位 | 1~4コーナーにおける通過順位 |
| 獲得本賞金 | |
| 1着タイム差 | |
| 着差 | |
| 単勝オッズ | |
| 単勝人気順位 |
プログラムで馬柱を再現
最後に馬柱データ構造のPython 3での実装例を紹介します。 以下のコードはAlphaImpactで実際に使っているコードの一部抜粋です。 このHorseTableクラスでは馬柱の表示しか実装していないので実質データ格納しかできませんが、AlphaImpactでは着順や走破タイム、払戻を扱いやすいデータ形式にして取得するメソッドなどが実装されており、開発の生産性向上に貢献しています。
class HorseTable(object):
"""
馬柱クラス
"""
def __init__(self, race, horses):
"""
:param race: Raceクラス
:type race: Race
:param horses: Horseクラスのリスト. 馬番順に格納
:type horses: list of Horse
"""
self.race = race
self.horse = horses
def print_table(self):
"""
簡易馬柱を表示する
"""
# レース名の表示
print(self.race.get_race_title())
# 馬番+馬名の表示
for i, horse in enumerate(self.horses):
horse_no = i + 1
print("{}. {}".format(horse_no, horse.get_name()))
class Race(object):
"""
レースクラス
"""
def __init__(self, race_key, program, holding, payback=None):
"""
:param race_key: レースキー
:param program: レース情報
:param holding: 開催情報
:param payback: 払戻情報
"""
self.race_key = race_key
self.program = program
self.holding = holding
self.payback = payback
def get_race_title(self):
"""
レース名を取得する
"""
return self.program["レース名"]
class Horse(object):
"""
馬クラス
"""
def __init__(self, horse_id, horse_profile, horse_race, race_results):
"""
:param horse_id: 血統登録番号
:param horse_profile: 馬属性情報
:param horse_race: 馬毎レース情報
:param race_results: 過去戦績のリスト
"""
self.horse_id = horse_id
self.horse_profile = horse_profile
self.horse_race = horse_race
self.race_results = race_results
def get_name(self):
"""
馬名を取得する
"""
return self.horse_profile["馬名"]
おわりに
今回はプログラム上で競馬データを見通しよく扱うための馬柱データ構造を紹介しました。 本記事で紹介したのはあくまで一例ですので、ご自身の持っているデータに合わせたオリジナルの馬柱を作成してみて下さい。 この地道な作業が良い予測モデルへの近道となるでしょう。
次回はHorseTableを使って特徴量の作成方法について説明していきます。
-
単位面積当たりに含まれる情報量が最も多い紙媒体の1つ ↩︎