概要
競馬 × AIの話題を見かけることがここ数年で増えましたが、全レースを対象としたものも多く、
個人的に気になる事例に絞って分析してみたい!と思い、今回netkeiba-scraperを使用していじってみました。
今回絞るテーマは「単勝1倍台の馬が出走するレースの買い方」です。
2019年は日本ダービーや阪神JF、有馬記念など、単勝1倍台の馬が馬券外に沈むケースも多く見られました。
あくまで単勝馬券で分析は進めました。
※SQLをPythonでいじる練習も兼ねていますので、コードの書き方ご指導いただけると嬉しいです!
netkeiba-scraperでデータ取得
競馬データのスクレイピングについては、偉大なる先人のコードを使用させていただきました。
[gitHubはこちら] (https://github.com/stockedge/netkeiba-scraper)
過去10年分のデータを取得しました。
特徴量抽出のためのgenfeature
は、処理に40日くらいかかりそうだったため使用しませんでした。
またJRAのクラス呼称が変更された(ex. 500万下→1勝クラス)ため、Scalaコードを直さないと適切にgenfeature
できなさそうです。
DLしたrace.dbファイルをPythonで読み込み、データ分析を開始します。
Jupyter NotebookでSQLiteを読み込む
import sqlite3 as sq
conn = sq.connect('race.db')
cur = conn.cursor()
cur.execute("SELECT r.race_id, i.race_name, r.order_of_finish, r.odds \
FROM race_result r INNER JOIN race_info i on r.race_id=i.id \
WHERE r.odds<2.0")
rows = cur.fetchall()
for row in rows:
print(row)
Jupyter Notebookのipynbファイルと同じ階層にrace.dbファイルを移し、読み込みます。
race.dbのうちレースのデータがrace_info
テーブル、race_result
テーブルに格納されているので、
欲しい情報を各テーブルから取得します。
※race_infoテーブル レースごとの情報が載っています
※race_resultテーブル 馬ごとの戦績が載っています
1倍台の人気で勝利した馬の期待値
# 勝ち数
cur.execute("SELECT r.odds, count(r.race_id) \
FROM race_result r \
WHERE r.odds<2.0 AND r.order_of_finish='1' \
GROUP BY r.odds ORDER BY r.odds ASC")
rows = cur.fetchall()
# 全レース
cur.execute("SELECT r.odds, count(r.race_id) \
FROM race_result r \
WHERE r.odds<2.0 \
GROUP BY r.odds ORDER BY r.odds")
rows2 = cur.fetchall()
# 期待値計算 もっと他に書きようありそう
try:
t11 = rows[0][0] * rows[0][1] / (rows2[0][0] * rows2[0][1]) * 100
t12 = rows[1][0] * rows[1][1] / (rows2[1][0] * rows2[1][1]) * 100
t13 = rows[2][0] * rows[2][1] / (rows2[2][0] * rows2[2][1]) * 100
t14 = rows[3][0] * rows[3][1] / (rows2[3][0] * rows2[3][1]) * 100
t15 = rows[4][0] * rows[4][1] / (rows2[4][0] * rows2[4][1]) * 100
t16 = rows[5][0] * rows[5][1] / (rows2[5][0] * rows2[5][1]) * 100
t17 = rows[6][0] * rows[6][1] / (rows2[6][0] * rows2[6][1]) * 100
t18 = rows[7][0] * rows[7][1] / (rows2[7][0] * rows2[7][1]) * 100
t19 = rows[8][0] * rows[8][1] / (rows2[8][0] * rows2[8][1]) * 100
except:
None
print('単勝1.1倍の馬券期待値は %d' %t11)
print('単勝1.2倍の馬券期待値は %d' %t12)
print('単勝1.3倍の馬券期待値は %d' %t13)
print('単勝1.4倍の馬券期待値は %d' %t14)
print('単勝1.5倍の馬券期待値は %d' %t15)
print('単勝1.6倍の馬券期待値は %d' %t16)
print('単勝1.7倍の馬券期待値は %d' %t17)
print('単勝1.8倍の馬券期待値は %d' %t18)
print('単勝1.9倍の馬券期待値は %d' %t19)
面白いことに、オッズが低いほど単勝期待値が高い(=1着になる可能性が高い)ことが分かりました。
やはり1.1倍ともなると歴史的名馬も多いからでしょうか。
似た条件で【オープンクラス以上(重賞含む)】に絞ると、1.1倍の期待値がさらに上昇しました。
cur.execute("SELECT r.odds, count(r.race_id) \
FROM race_result r INNER JOIN race_info i on r.race_id=i.id \
WHERE r.odds<2.0 AND r.order_of_finish='1' AND i.race_class like '%オープン%' \
GROUP BY r.odds ORDER BY r.odds ASC")
人気別の成績を調べる
単勝1倍台の馬が出走するレースでは、どの馬が勝利することが多いのでしょうか?
2番人気や3番人気に妙味はあるのでしょうか?
上までの分析と違い、【単勝1倍台の馬がいるレースに限定】しなければ計算できないため、
WHERE ~ IN (SELECT ~~)
の記法でサブクエリを使用しました。
# 単勝1倍台の馬がいるレースのうち人気別勝ち数
cur.execute("SELECT r.popularity, count(r.race_id) \
FROM race_result r \
WHERE r.order_of_finish='1' AND r.race_id IN (SELECT race_id from race_result WHERE odds<2.0 AND popularity='1') \
GROUP BY r.popularity ORDER BY r.popularity ASC")
rows = cur.fetchall()
# 単勝1倍台の馬がいる全レース
cur.execute("SELECT count(r.race_id) FROM race_result r WHERE r.odds<2.0 AND r.popularity='1'")
rows2 = cur.fetchall()
for row in rows:
print(row)
print(rows2[0][0])
全7,619レース中、約半数を1番人気が勝利しています。
2番人気は単勝6倍以上、3番人気は10倍近くつかないと期待値100を超えません。
オッズに頭数を掛け、レース数で割る形で期待値を計算しましたが、32と酷い有様でした。
まとめ
競馬場やダート/芝、馬齢、馬番、馬の脚質を細分化しなければ、オッズを見ても儲からない、というのが今回の結果でした。
また競馬には多様な馬券の種類があるため、【単勝1倍台の馬が出走するレースで最も期待値の高い馬券種はどれか?】など、
もっと深く分析できそうです。
Python(Jupiter Notebook)でSQLを抽出する要領はつかめたので、引き続き分析してみようと思います。