競馬予想AIの作成④(取得した競馬データの確認)

機械学習

はじめに

前回取得したデータでAIモデルを作成する前に、データの状態を確認します。
欠損値や異常値の有無を確認し、除外あるいは補完を行います。また、各データの統計情報と相関係数をCSVファイルに保存し、のちほど確認できるようにしておきます。

取得したデータには天候(小雨、雨、曇、晴、雪、小雪)、状態(良、重、ダード、芝)が含まれています。One-Hotエンコーディングを天候や状態に適用することで、天候であれば小雨、雨、曇、などで新しい列を作成して該当する項目に1、それ以外の項目には0を割り当てます。

天候にOne-Hotエンコーディングを適用した場合は下記のようになります。

One-Hotエンコーディング適用前

One-Hotエンコーディング適用後

プログラムの概要

該当行 概要
1~4 ライブラリの読み込み
6~12 レース結果のCSVファイルを読み込みカラム名を設定する
14~17 各列の型、要約統計量、各列の欠損数を確認する
19~24 一部でも欠損している行、馬体重が’計不’になっている行を削除する
29~40 開催日の型をobjectからdatetime64に変換する
タイムのmm:ssの表記をssに変換する(01:22→82)
42~50 馬体重の”()”を削除して現在の体重のみを取得する
52~55 一部の値は’2,010.0’となっているので、’2010’に変換する
58~63 One-Hotエンコーディングを行う関数
66~68 騎手と馬名のユニーク数/出現数を保存する
70~86 馬名、天候、性齢、状態、馬場、騎手でOne-Hotエンコーディングを実行する
88~90 One-Hotエンコーディング後の各列の最大値/最小値/平均/標準偏差/データ数、相関係数をCSVに保存する
92~95 各列同士の相関係数をヒートマップで表示する

作成したプログラム

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import japanize_matplotlib

# レース結果を読み込む
df_race_result = pd.read_csv('./2010_1_2010_12_レースの結果.csv', encoding='cp932')

# カラム名を設定する
df_race_result.columns = ['タイム', '馬名', '性齢', '斤量', '騎手', '単勝',
                          '人気', '馬体重', '距離', '天候', '馬場', '状態',
                          '開催日', 'レース名', '開催場所']

# 各列の型、要約統計量、各列の欠損数を確認する
print(df_race_result.dtypes)
print(df_race_result.describe())
print(df_race_result.isnull().sum())

# 欠損している行を削除する
df_race_result = df_race_result.dropna()

# 馬体重が'計不'になっている行を削除する
drop_target = df_race_result.index[(df_race_result['馬体重'] == '計不')]
df_race_result = df_race_result.drop(drop_target)

# 行を削除したのでindexをリセットする
df_race_result = df_race_result.reset_index(drop=True)

# 開催日の型をobjectからdatetime64に変換する
df_race_result['開催日'] = pd.to_datetime(df_race_result['開催日'], format='%Y年%m月%d日')

# 分秒の表記を秒に変換する
time_list = []
df_time = df_race_result['タイム'].str.split(':')
for i in df_time:
    times = float(i[0]) * 60 + float(i[1])
    time_list.append(times)

df_times_list = pd.DataFrame(time_list)
df_race_result['タイム'] = df_times_list

# 馬体重の()を削除して現在の体重を取得する
weight_list = []
df_weight = df_race_result['馬体重'].str.split('(')
for i in df_weight:
    weight = float(i[0])
    weight_list.append(weight)

df_weight_list = pd.DataFrame(weight_list)
df_race_result['馬体重'] = df_weight_list

# 単勝の型をobjectからfloatに変換する
# 一部の値は'2,010.0'となっているので、'2010'に変換する
df_race_result = df_race_result.replace('2,010.0', '2010')
df_race_result['単勝'] = df_race_result['単勝'].astype(float)


# One-Hotエンコーディングを行う関数
def one_hot_enc(df, column):
    df_dummy = pd.get_dummies(df[column], prefix=column)    # get_dummiesでone hot encodingを取得する
    df_drop = df.drop([column], axis=1)                     # 元列を削除する
    df = pd.concat([df_drop, df_dummy], axis=1)             # df_dropとdf_dummyを横結合する
    return df


# 馬名、騎手のユニーク数,出現数をカウントする
df_race_result['馬名'].value_counts().to_csv('./馬名.csv', encoding='cp932', errors='ignore')
df_race_result['騎手'].value_counts().to_csv('./騎手.csv', encoding='cp932', errors='ignore')

# 馬名をone_hot_encodingする
# df_race_result = one_hot_enc(df_race_result, '馬名')

# 天候をone_hot_encodingする
df_race_result = one_hot_enc(df_race_result, '天候')

# 性齢をone_hot_encodingする
df_race_result = one_hot_enc(df_race_result, '性齢')

# 状態をone_hot_encodingする
df_race_result = one_hot_enc(df_race_result, '状態')

# 馬場をone_hot_encodingする
df_race_result = one_hot_enc(df_race_result, '馬場')

# 騎手をone_hot_encodingする
df_race_result = one_hot_enc(df_race_result, '騎手')

# 各列の最大値、最小値、平均、標準偏差、データ数、相関係数を保存する
df_race_result.describe().to_csv('./要約統計量.csv', encoding='cp932', errors='ignore')
df_race_result.corr().to_csv('./相関係数.csv', encoding='cp932', errors="ignore")

# 各列同士の相関係数をヒートマップで表示する
fig, ax = plt.subplots(figsize=(12, 12))
sns.heatmap(df_race_result.corr(), vmax=1, vmin=-1, center=0, annot=True)
plt.show()

プログラムの実行結果

4つのCSVファイルと各列同士の相関係数のヒートマップを出力します。
出力されるCSVファイルは以下の4つなります。

CSVファイル名 概要
馬名.csv 馬ごとの出走数
騎手.csv 騎手ごとの騎乗数
要約統計量.csv 最大値、最小値、平均、標準偏差、データ数
相関係数.csv 各列同士の相関係数

馬名と騎手のCSVファイルの一部

相関係数のCSVファイルの一部

要約統計量のCSVファイルの一部

ヒートマップ

プログラム作成時にハマったところ

馬名でOne-Hotエンコーディングすると処理が終わらない

2010年1月~12月のレースで出場した馬頭数は、20,891でした。
馬名でOne-Hotエンコーディングすると、メモリの使用容量が99%になり処理が終わりませんでした。71行目の馬名でのOne-Hotエンコーディングはコメントアウトしています。

取得したデータの確認結果

取得した2010年1月~12月のレース情報で、欠損が存在する列と、欠損数(行数)は以下でした。

  • タイム列:1978行
  • 単勝列:18行
  • 人気列:1461行
  • 馬体重:40行

タイム列が欠損している情報は、単勝列や人気列も欠損していることが多く、出走を取りやめた可能性があったので、データを補完するのではなく行ごと削除しました。馬体重列の値が「計不」となっている行がありました。この行もデータを補完するのではなく行ごと削除しました。

作成するAIモデルの目的変数は「タイム」にする予定です。「タイム」と相関関係が高いのは0.97の「距離」でした。この結果は予想通りでした。次に相関関係が高いのは「斤量」と「馬体重」でした。ただ、それぞれの相関係数は0.15、0.14でほとんど関係はなさそうです。天候、馬場、状態、騎手などの相関係数は、ほぼ0でした。

次回、試すこと

整形したデータとxgboostとligthgbmで、タイムを予測するAIモデルを作成してみます。

コメント

タイトルとURLをコピーしました