JvLink To Importer

重賞レース予想に効く季節性・クラス別特徴量設計

重賞レース予想に効く季節性・クラス別特徴量設計

「春の天皇賞でステイヤー血統が好走しやすい」「夏のローカル開催で前残りが効く」── 競馬の世界では肌感覚として共有される傾向ですが、データで裏を取って特徴量にすればモデルの精度に直結します。この記事では季節性・クラス変化・距離適性の集計テンプレを紹介します。


季節性の集計

季節と着順の相関を見るには、まず月またはクォーターで集計するのが手早い。

-- 月別の各馬の連対率
SELECT
horse_id,
EXTRACT(MONTH FROM race_date) AS month,
COUNT(*) AS races,
AVG((finish_position <= 2)::int) AS rentai_rate
FROM race_results
WHERE finish_position IS NOT NULL
GROUP BY horse_id, EXTRACT(MONTH FROM race_date)
HAVING COUNT(*) >= 3;  -- 最低3走でフィルタ

季節性は「特定の馬の傾向」より「血統 × 季節」「コース × 季節」のような集約軸で見る方が安定します。

-- 種牡馬 × 季節 の連対率
SELECT
s.sire_name,
CASE
WHEN EXTRACT(MONTH FROM r.race_date) IN (3,4,5) THEN '春'
WHEN EXTRACT(MONTH FROM r.race_date) IN (6,7,8) THEN '夏'
WHEN EXTRACT(MONTH FROM r.race_date) IN (9,10,11) THEN '秋'
ELSE '冬'
END AS season,
COUNT(*) AS races,
ROUND(AVG((r.finish_position <= 2)::int)::numeric, 3) AS rentai_rate
FROM race_results r
JOIN horses h USING (horse_id)
JOIN sires s ON h.sire_id = s.sire_id
GROUP BY s.sire_name, season
HAVING COUNT(*) >= 30
ORDER BY s.sire_name, season;

夏の小回り(札幌・函館)に強い種牡馬、春の長距離 G1 で安定する種牡馬、といった傾向が見えてきます。


クラス変化の特徴量

「昇級初戦は割引」「降級馬は買い」など、クラス間の移動は予想で重要なサインです。

-- 前走のクラスとの差分
SELECT
race_id,
horse_id,
class_grade,
LAG(class_grade) OVER (PARTITION BY horse_id ORDER BY race_date) AS prev_class,
class_grade - LAG(class_grade) OVER (PARTITION BY horse_id ORDER BY race_date) AS class_diff
FROM race_results;

class_diff > 0 → 昇級、class_diff < 0 → 降級。これだけで「初の昇級戦かどうか」が判定できます。

昇級初戦の連対率を集計

WITH class_change AS (
SELECT
horse_id,
race_id,
finish_position,
class_grade
- LAG(class_grade) OVER (PARTITION BY horse_id ORDER BY race_date) AS class_diff
FROM race_results
)
SELECT
CASE
WHEN class_diff > 0 THEN '昇級'
WHEN class_diff = 0 THEN '同クラス'
WHEN class_diff < 0 THEN '降級'
END AS class_change,
COUNT(*) AS races,
AVG((finish_position <= 3)::int) AS fukusho_rate
FROM class_change
WHERE class_diff IS NOT NULL
GROUP BY 1;

肌感とおりに「昇級初戦は複勝率が下がる」「降級は上がる」が数字で確認できます。


距離適性の指標化

過去走から各馬の「ベスト距離」を推定する単純な方法。

# 馬ごとに距離別の連対率を集計し、最大値を取る距離をベスト距離とする
def best_distance(group):
rates = group.groupby('distance')['top2'].mean()
if len(rates) == 0:
return None
return rates.idxmax()

df['top2'] = (df['finish_position'] <= 2).astype(int)
horse_best = df.groupby('horse_id').apply(best_distance)
df = df.merge(horse_best.rename('best_distance'), on='horse_id')

# 今走の距離とベスト距離の差
df['distance_diff_from_best'] = (df['distance'] - df['best_distance']).abs()

距離変更の幅を「ベスト距離からの絶対差」として持っておくと、初挑戦距離に弱い馬を炙り出せます。


重賞専用の特徴量

重賞では一般戦と評価軸が違うので、専用の特徴量が効きます。

  • G1/G2/G3 別の過去成績: 重賞経験のない馬は割引
  • 同条件 G1 の過去着順: 春天 → 春天、ダービー → 菊花賞 など同系列レースの戦績
  • ローテーション: 前走からの間隔(中3週・中9週など)と着順の関係
  • トップジョッキー騎乗フラグ: 重賞では騎手の腕の影響が一般戦より大きい
-- 各馬の重賞 (G1/G2/G3) 経験回数と連対率
SELECT
horse_id,
COUNT(*) FILTER (WHERE grade IN ('G1','G2','G3')) AS jusho_races,
AVG((finish_position <= 2)::int)
FILTER (WHERE grade IN ('G1','G2','G3')) AS jusho_rentai_rate
FROM race_results
GROUP BY horse_id;

まとめ

  • 季節性は「血統 × 季節」「コース × 季節」のような集約軸で見ると安定
  • クラス変化 (class_diff) は LAG ウィンドウ関数で簡潔に取れる
  • 距離適性は「ベスト距離との差」で持っておくと汎用性が高い
  • 重賞は専用特徴量(重賞経験、ローテ、騎手)を別建てで用意

季節性とクラス変化はベテラン予想家が頭で計算している部分なので、データで自動化できると人間の労力を予想の本質に集中できます。

この記事をシェア

Post