Chapter 10無料公開

【新】modules/constants/の解説

競馬予想で始めるデータ分析・機械学習
競馬予想で始めるデータ分析・機械学習
2022.07.21に更新

概要

modules/constants/には定数を定義するモジュール群が入っていて、ディレクトリ構成は以下のようになっています。

modules/constants
├── __init__.py
├── _horse_results_cols.py      ・・・馬の過去成績テーブルのサイト上の列名を定数化する
├── _local_paths.py             ・・・ローカルファイルパスを定数化する
├── _master.py                  ・・・レース開催場所やレースの階級などを定数化する
├── _results_cols.py            ・・・レース結果テーブルのサイト上の列名を定数化する
└── _url_paths.py               ・・・netkeiba.com上のURLパスを定数化する

サイト上の列名やファイルパスを定数として持っておくことで、

  • サイトの仕様変更で列名が変更された場合に、これらのモジュールを修正するだけでよくなる
  • 誤ったファイルパスが入力されることを防ぐ

など、様々なメリットがあります。

ソースコード解説

__init__.py

ソースコード
__init__.py
from ._horse_results_cols import HorseResultsCols
from ._results_cols import ResultsCols
from ._master import Master
from ._local_paths import LocalPaths
from ._url_paths import UrlPaths

ディレクトリ構成を考えると、例えばHorseResultsCols.DATEという変数を使いたい時には、次のようにインポートする必要があります。

main.ipynb
from modules.constants._horse_results_cols import HorseResultsCols

HorseResultscols.DATE

しかし、これだとパッケージが複雑になってディレクトリ階層が増えるたびに、インポート文が長くなってしまいます。

そこで、上のinitファイルを書くことで、initファイル内でインポートされているオブジェクトをconstantsから直接呼び出すことができます。

main.ipynb
from modules.constants import HorseResultsCols

HorseResultscols.DATE

このようにして、他のディレクトリについても、インポート時の階層は一つで済むようにしています。

また、その場合、_horse_results_cols.pyは外部から直接使うことがないので、アンダースコア(_)を付けています。

「そもそも__init__.pyとは何なのか」については、例えばこちらの記事などで詳しく解説されています。

https://qiita.com/msi/items/d91ea3900373ff8b09d7

_horse_results_cols.py

ソースコード
_horse_results_cols.py
import dataclasses


@dataclasses.dataclass(frozen=True)
class HorseResultsCols:
    """
    サイト上のテーブル列名を、定数として持っておく。
    """
    DATE: str = '日付'
    PLACE: str = '開催'
    WEATHER: str = '天気'
    R: str = 'R'
    RACE_NAME: str = 'レース名'
    N_HORSES: str = '頭数'
    WAKUBAN: str = '枠番'
    UMABAN: str = '馬番'
    TANSHO_ODDS: str = 'オッズ'
    POPULARITY: str = '人気'
    RANK: str = '着順'
    JOCKEY: str = '騎手'
    KINRYO: str = '斤量'
    RACE_TYPE_COURSE_LEN: str = '距離'
    GROUND_STATE: str = '馬場'
    TIME: str = 'タイム'
    RANK_DIFF: str = '着差'
    CORNER: str = '通過'
    PACE: str = 'ペース'
    NOBORI: str = '上り'
    WEIGHT_AND_DIFF: str = '馬体重'
    PRIZE: str = '賞金'

馬の過去成績テーブルの列名について、netkeiba.com上での表記を定数化しています。
こうすることで、サイト上の仕様変更が起きて、例えば列名が「日付」→「開催日」に変わった、などの場合にこのファイルの定義を少し変えるだけで済みます。

このモジュールで使われているdataclassとは、データを格納するためのクラスを簡単に定義できる機能です。まさに、今回のように「定数を定義したい」という時など、特に処理を行う訳ではない場合に使います。

使い方は、上のコードのように、クラス定義の上に@dataclasses.dataclassを付けるだけです。このように、クラスの上に付いている@を、デコレータといいます。

オプションで付いている(frozen=True)は、クラス内で定義する変数をイミュータブル(外から書き換え不可能)にするためのものです。

_local_paths.py

ソースコード
_local_paths.py
import os
import dataclasses

@dataclasses.dataclass(frozen=True)
class LocalPaths:
    # パス
    ## プロジェクトルートの絶対パス
    BASE_DIR: str = os.path.abspath('./')
    ## dataディレクトリまでの絶対パス
    DATA_DIR: str = os.path.join(BASE_DIR, 'data')
    ### HTMLディレクトリのパス
    HTML_DIR: str = os.path.join(DATA_DIR, 'html')
    HTML_RACE_DIR: str = os.path.join(HTML_DIR, 'race')
    HTML_HORSE_DIR: str = os.path.join(HTML_DIR, 'horse')
    HTML_PED_DIR: str = os.path.join(HTML_DIR, 'ped')
    
    ### rawディレクトリのパス
    RAW_DIR: str = os.path.join(DATA_DIR, 'raw')
    RAW_RESULTS_PATH: str = os.path.join(RAW_DIR, 'results.pickle')
    RAW_RACE_INFO_PATH: str = os.path.join(RAW_DIR, 'race_info.pickle')
    RAW_RETURN_TABLES_PATH: str = os.path.join(RAW_DIR, 'return_tables.pickle')
    RAW_HORSE_RESULTS_PATH: str = os.path.join(RAW_DIR, 'horse_results.pickle')
    RAW_PEDS_PATH: str = os.path.join(RAW_DIR, 'peds.pickle')
    
    ### masterディレクトリのパス
    MASTER_DIR: str = os.path.join(DATA_DIR, 'master')

ローカルのディレクトリ・ファイルのパスを定義するクラスです。
こちらも、定数としてパスを定義しておくことで、良い意味で使う側に自由度を持たせず、間違ったパスが入力されたり、存在しないパスが入力されたりすることを防ぎます。

_master.py

ソースコード
_master.py
import dataclasses
from types import MappingProxyType

@dataclasses.dataclass(frozen=True)
class Master:
    PLACE_DICT: dict = MappingProxyType({
        '札幌':'01',
        '函館':'02',
        '福島':'03',
        '新潟':'04',
        '東京':'05',
        '中山':'06',
        '中京':'07',
        '京都':'08',
        '阪神':'09',
        '小倉':'10',
        '門別':'30',
        '旭川':'34',
        '盛岡':'35',
        '水沢':'36',
        '浦和':'42',
        '船橋':'43',
        '大井':'44',
        '川崎':'45',
        '金沢':'46',
        '笠松':'47',
        '名古屋':'48',
        '園田':'50',
        '姫路':'51',
        '福山':'53',
        '高知':'54',
        '佐賀':'55',
        '荒尾':'56',
        '札幌(地)':'58',
        '香港':'60',
        'フランス':'61',
        'オースト':'62',
        'イギリス':'63',
        'シャティ':'64',
        'アラブ首':'65',
        'メイダン':'66',
        'ドイツ':'67',
        'アイルラ':'68',
        'アメリカ':'69',
        'ロンシャ':'70',
        'イタリア':'71',
        'シンガポ':'72',
        'カナダ':'73',
        'シャンテ':'74',
        '韓国':'75',
        'フレミン':'76',
        'ニュージ':'77',
        'アスコッ':'78',
        'デルマー':'79',
        'サンタア':'80',
        'コーフィ':'81',
        'ベルモン':'82',
        'ドーヴィ':'83',
        'ランドウ':'84',
        'ヨーク':'85',
        'レパーズ':'86',
        'チャーチ':'87',
        'サンダウ':'88'
        })

    RACE_TYPE_DICT: dict = MappingProxyType({
        '芝': '芝',
        'ダ': 'ダート',
        '障': '障害',
        })
    
    WEATHER_LIST: tuple = ('晴', '曇', '小雨', '雨', '小雪', '雪')
    
    GROUND_STATE_LIST: tuple = ('良', '稍重', '重', '不良')
    
    SEX_LIST: tuple = ('牡', '牝', 'セ')
    
    AROUND_LIST: tuple = ('右', '左', '直線', '障害')
    
    RACE_CLASS_LIST: tuple = ('新馬', '未勝利', '1勝クラス', '2勝クラス', '3勝クラス', 'オープン', '障害')

開催会場や天気、レースのクラスなどについて、netkeiba.com上での表記を定数化したクラスです。
開催場所は、後に開催場所idと対応させる必要があるので、辞書型として対応関係を保持しています。MappingProxyTypeは、辞書型をイミュータブル(外部から書き換え不可能)にしたものです。
レース種別(RACE_TYPE_DICT)は、取得元によってダートが「ダ」と表記されていたり、「ダート」と表記されていたりするので、その対応関係を記録しています。
他の変数については、タプル型(書き換え不可能な配列型)で定義しています。

_results_cols.py

ソースコード
_results_cols.py
import dataclasses

@dataclasses.dataclass(frozen=True)
class ResultsCols:
    """
    サイト上のテーブル列名を、定数として持っておく。
    """
    RANK: str = '着順'
    WAKUBAN: str = '枠番'
    UMABAN: str = '馬番'
    HORSE_NAME: str = '馬名'
    SEX_AGE: str = '性齢'
    KINRYO: str = '斤量'
    JOCKEY: str = '騎手'
    TIME: str = 'タイム'
    TANSHO_ODDS: str = '単勝'
    POPULARITY: str = '人気'
    WEIGHT_AND_DIFF: str = '馬体重'
    TRAINER: str = '調教師'

_horse_results_cols.pyと同様、レース結果テーブルの列名を定数化したクラスです。

_url_paths.py

ソースコード
_url_paths.py
import dataclasses


@dataclasses.dataclass(frozen=True)
class UrlPaths:
    DB_DOMAIN: str = 'https://db.netkeiba.com/'
    # レース結果テーブル、レース情報テーブル、払い戻しテーブルが含まれるページ
    RACE_URL: str = DB_DOMAIN + 'race/'
    # 馬の過去成績テーブルが含まれるページ
    HORSE_URL: str = DB_DOMAIN + 'horse/'
    # 血統テーブルが含まれるページ
    PED_URL: str = HORSE_URL + 'ped/'
    
    TOP_URL: str = 'https://race.netkeiba.com/top/'
    # 開催日程ページ
    CALENDAR_URL: str = TOP_URL + 'calendar.html'
    # レース一覧ページ
    RACE_LIST_URL: str = TOP_URL + 'race_list.html'
    
    # 出馬表ページ
    SHUTUBA_TABLE: str = 'https://race.netkeiba.com/race/shutuba.html'

netkeiba.com上のURLパスを定数化したクラスです。