AlphaImpact 会社概要 事業内容 開発情報 お問い合わせ

第4回 馬柱で競馬データを整理する

2017/02/02更新

NUKUI

修士論文の執筆が忙しくだいぶ間が空いてしまいましたが、第4回の記事では各ソースから収集してきたデータを整理する方法について書いていきます。

まずは馬柱をつくる

競馬の1レースが持つ情報には、競馬場や距離などのレースに関する情報、また出走する各馬の馬番、負担重量、騎手、プロフィール、過去戦績などがあります。さらに各戦績はレース情報、負担重量、騎手、着順・タイムなどの結果を持っており、非常に複雑です。

機械学習アルゴリズムの入力となる特徴量を作成するためには、それら複雑なデータを整形していく必要があります。例えば、分+秒形式のタイムを秒に変換したり、走破タイムをレース内で標準化したり、過去の着順から勝率を計算するなどです。それらの泥臭いデータ整形をスクリプトやSQLのクエリとしてベタ書きをしていくと、保守性が失われ、後々手の施しようがないプログラムになってしまいます(経験談)。

そこでAlphaImpactでは収集してきたデータから『馬柱』を作ることでデータの管理や生成の効率化を実現しています。馬柱とは競馬新聞1に載っているレースに必要な情報がまとまっている表のことです。『馬柱』をつくるといっても実際に競馬新聞をつくるのではなく、馬柱を模倣したデータ構造をプログラムで再現します。煩雑な生データを馬柱で包むことによって、予測やデータ成形のインターフェースを統一することができるので、プログラムの見通しも良くなります。例えばAlphaImpactでは、レースの予測を出すときは predict(horse_table) 、特徴ベクトルを生成する場合はcreate_data(horse_table) のように簡潔なコードで記述できています。

馬柱を要素に分解

馬柱は以下の図のように要素を分解できます。

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. 単位面積当たりに含まれる情報量が最も多い紙媒体の1つ ↩︎