データ分析入門

JRDB-APIで取得したデータを使って、Pythonで基本的なデータ分析を行います。

必要なライブラリ

pip install pandas numpy matplotlib seaborn requests

Step 1: データの取得と整形

過去1ヶ月分の成績データ(/dse)と 番組データ(/dra)を取得し、 レースキーで結合して pandas の DataFrame にまとめます。 着順・IDM・人気は成績データ側、距離・トラック種別は番組データ側にあります。

import pandas as pd
import requests
from datetime import datetime, timedelta

# ダッシュボードで取得した値をそれぞれ貼り付け
API_KEY  = "API_KEY"     # APIキー(アプリ共通)
AUTH_KEY = "YOUR_AUTH_KEY"    # 認証キー (x-user-key)

def fetch(endpoint, start_date, end_date):
    """過去データを期間指定で取得して DataFrame にする"""
    url = f"https://dev.api.bigtime.world/v1/{endpoint}?api_key={API_KEY}"

    headers = {
        "Content-Type": "application/json",
        "x-user-key": AUTH_KEY
    }

    payload = {
        "start_racedate": start_date,
        "end_racedate": end_date
    }

    response = requests.post(url, json=payload, headers=headers)
    data = response.json()

    # status は "success" / "error" の文字列
    # 成功時は result.status == 200。エラー時はトップレベルに error/message が返る
    if data.get("error") or data["result"]["status"] != 200:
        raise Exception(data.get("message", "Unknown error"))

    return pd.DataFrame(data["result"]["data"])

# 過去1ヶ月のデータを取得
end_date = datetime.now().strftime("%Y%m%d")
start_date = (datetime.now() - timedelta(days=30)).strftime("%Y%m%d")

se = fetch("dse", start_date, end_date)   # 成績(着順・IDM・確定人気)
ra = fetch("dra", start_date, end_date)   # 番組(距離・トラック種別・クラス)

# レース単位の条件を1頭ごとの成績に付ける
df = se.merge(
    ra[['racekey', 'distance', 'track_type', 'racecourse_cd', 'class_cd']],
    on='racekey',
    how='left'
)

print(f"取得件数: {len(df)}件")
print(f"カラム数: {len(df.columns)}個")
print(df.head())

Step 2: 基本統計量の確認

データの基本的な統計量を確認します。

# 数値カラムの基本統計量
print(df.describe())

# IDM(能力指数)の分布
print("\nIDM統計:")
print(f"  平均: {df['idm'].mean():.2f}")
print(f"  標準偏差: {df['idm'].std():.2f}")
print(f"  最小: {df['idm'].min()}")
print(f"  最大: {df['idm'].max()}")

# 着順分布
print("\n着順分布:")
print(df['order_of_finish'].value_counts().sort_index().head(10))

Step 3: データの可視化

matplotlibとseabornを使ってデータを可視化します。

import matplotlib.pyplot as plt
import seaborn as sns

# 日本語フォント設定(環境に応じて変更)
plt.rcParams['font.family'] = 'MS Gothic'

fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 1. IDMの分布
axes[0, 0].hist(df['idm'].dropna(), bins=30, edgecolor='black')
axes[0, 0].set_title('IDM分布')
axes[0, 0].set_xlabel('IDM')
axes[0, 0].set_ylabel('件数')

# 2. IDMと着順の関係
df_top5 = df[df['order_of_finish'] <= 5]
axes[0, 1].boxplot([
    df[df['order_of_finish'] == i]['idm'].dropna()
    for i in range(1, 6)
])
axes[0, 1].set_title('着順別IDM分布')
axes[0, 1].set_xlabel('着順')
axes[0, 1].set_ylabel('IDM')

# 3. 馬体重の分布
axes[1, 0].hist(df['horse_weight'].dropna(), bins=30, edgecolor='black')
axes[1, 0].set_title('馬体重分布')
axes[1, 0].set_xlabel('馬体重 (kg)')
axes[1, 0].set_ylabel('件数')

# 4. 人気と着順の関係
win_rate = df.groupby('confirmed_win_popularity').apply(
    lambda x: (x['order_of_finish'] == 1).sum() / len(x) * 100
).head(10)
axes[1, 1].bar(win_rate.index, win_rate.values)
axes[1, 1].set_title('人気別勝率')
axes[1, 1].set_xlabel('人気')
axes[1, 1].set_ylabel('勝率 (%)')

plt.tight_layout()
plt.savefig('analysis_result.png', dpi=150)
plt.show()

Step 4: 相関分析

各指標と着順の相関を分析します。

# 分析対象のカラム
target_columns = [
    'idm', 'horse_weight', 'confirmed_win_popularity',
    'confirmed_win_odds', 'order_of_finish'
]

# 相関行列の計算
correlation = df[target_columns].corr()

# ヒートマップで可視化
plt.figure(figsize=(8, 6))
sns.heatmap(
    correlation,
    annot=True,
    cmap='coolwarm',
    center=0,
    fmt='.2f'
)
plt.title('相関行列')
plt.tight_layout()
plt.savefig('correlation_heatmap.png', dpi=150)
plt.show()

# 着順との相関(絶対値でソート)
print("\n着順との相関係数:")
correlations = correlation['order_of_finish'].drop('order_of_finish')
print(correlations.abs().sort_values(ascending=False))

分析のポイント

  • • IDMが高い馬は着順が良い傾向がある(負の相関)
  • • 人気と着順には強い相関がある
  • • オッズが低い(人気がある)馬ほど着順が良い

Step 5: 条件別分析

芝・ダート、距離などの条件別に分析します。

# トラック種別(芝/ダート)で分析
def analyze_by_track(df):
    # track_type: 1=芝, 2=ダート, 3=障害(/dra の番組データ)
    df['track_label'] = df['track_type'].astype(str).map(
        {'1': '芝', '2': 'ダート', '3': '障害'}
    )

    result = df.groupby('track_label').agg({
        'idm': 'mean',
        'horse_weight': 'mean',
        'order_of_finish': lambda x: (x == 1).sum() / len(x) * 100
    }).rename(columns={'order_of_finish': 'win_rate'})

    return result

track_analysis = analyze_by_track(df)
print("トラック別分析:")
print(track_analysis)

# 距離別分析
def analyze_by_distance(df):
    # 距離カテゴリを作成
    df['distance_category'] = pd.cut(
        df['distance'].astype(int),
        bins=[0, 1400, 1800, 2200, 4000],
        labels=['短距離', 'マイル', '中距離', '長距離']
    )

    result = df.groupby('distance_category').agg({
        'idm': 'mean',
        'horse_weight': 'mean',
        'order_of_finish': lambda x: (x == 1).sum() / len(x)
    })

    return result

distance_analysis = analyze_by_distance(df)
print("\n距離別分析:")
print(distance_analysis)

次のステップ