worldcup-api / predict.py
wihaha's picture
feat(worldcup): include odds and weather summaries
336608c
Raw
History Blame Contribute Delete
5.73 kB
import os
import joblib
import pandas as pd
from data_loader import DataLoader
from feature_engineer import FeatureEngineer
from rule_based_model import RuleBasedModel
def get_predictions(model_dir='artifacts'):
loader = DataLoader()
rankings = loader.get_rankings()
form = loader.get_team_form()
teams = loader.get_teams()
matches = loader.get_matches()
try:
manual_features = loader.get_manual_features()
except Exception as e:
print(f"WARNING: Could not load manual features: {e}")
manual_features = None
odds_summary = loader.get_latest_odds_summary()
weather_summary = loader.get_latest_weather_summary()
loader.close()
engineer = FeatureEngineer(rankings, form, teams, manual_features)
# Select upcoming matches
upcoming = matches[matches['status'].isin(['scheduled', 'active'])].copy()
if len(upcoming) == 0:
return {"predictions": [], "skipped": []}
X_pred, _ = engineer.build_dataset(upcoming)
# Load model
model_path = os.path.join(model_dir, 'rule_based_baseline.pkl')
if not os.path.exists(model_path):
raise FileNotFoundError(f"Model not found at {model_path}. Run train.py first.")
clf = joblib.load(model_path)
# Predict probabilities
probs = clf.predict_proba(X_pred)
X_pred['prob_away_win'] = probs[:, 0]
X_pred['prob_draw'] = probs[:, 1]
X_pred['prob_home_win'] = probs[:, 2]
# Merge back to upcoming by match_id
upcoming = upcoming.merge(X_pred[['match_id', 'prob_away_win', 'prob_draw', 'prob_home_win']], left_on='id', right_on='match_id', how='left')
results = []
skipped = []
for _, row in upcoming.iterrows():
if pd.isna(row['prob_home_win']):
# Determine reason
reason = "unknown"
if pd.isna(row['home_team_id']) or pd.isna(row['away_team_id']):
reason = "missing_team_placeholder"
else:
reason = "missing_features"
skipped.append({
"match_id": row['id'],
"home_team_id": row['home_team_id'] if not pd.isna(row['home_team_id']) else None,
"away_team_id": row['away_team_id'] if not pd.isna(row['away_team_id']) else None,
"reason": reason
})
continue
# Check if manual features were applied
applied = False
if manual_features is not None and row['id'] in manual_features.index:
applied = True
odds_rows = []
if odds_summary is not None and not odds_summary.empty:
match_odds = odds_summary[odds_summary['match_id'] == row['id']]
for _, odds in match_odds.iterrows():
odds_rows.append({
"bookmaker_key": odds.get("bookmaker_key"),
"bookmaker_title": odds.get("bookmaker_title"),
"market_key": odds.get("market_key"),
"market_title": odds.get("market_title"),
"home_odds": float(odds["home_odds"]) if not pd.isna(odds.get("home_odds")) else None,
"draw_odds": float(odds["draw_odds"]) if not pd.isna(odds.get("draw_odds")) else None,
"away_odds": float(odds["away_odds"]) if not pd.isna(odds.get("away_odds")) else None,
"last_update": str(odds.get("last_update")) if not pd.isna(odds.get("last_update")) else None,
})
weather = None
if weather_summary is not None and not weather_summary.empty and row['id'] in weather_summary.index:
w = weather_summary.loc[row['id']]
weather = {
"forecast_time": str(w.get("forecast_time")) if not pd.isna(w.get("forecast_time")) else None,
"temperature_c": float(w["temperature_c"]) if not pd.isna(w.get("temperature_c")) else None,
"apparent_temperature_c": float(w["apparent_temperature_c"]) if not pd.isna(w.get("apparent_temperature_c")) else None,
"humidity_pct": float(w["humidity_pct"]) if not pd.isna(w.get("humidity_pct")) else None,
"precipitation_probability_pct": float(w["precipitation_probability_pct"]) if not pd.isna(w.get("precipitation_probability_pct")) else None,
"precipitation_mm": float(w["precipitation_mm"]) if not pd.isna(w.get("precipitation_mm")) else None,
"wind_speed_kmh": float(w["wind_speed_kmh"]) if not pd.isna(w.get("wind_speed_kmh")) else None,
"wind_gusts_kmh": float(w["wind_gusts_kmh"]) if not pd.isna(w.get("wind_gusts_kmh")) else None,
"weather_code": int(w["weather_code"]) if not pd.isna(w.get("weather_code")) else None,
}
results.append({
"match_id": row['id'],
"home_team_id": row['home_team_id'],
"away_team_id": row['away_team_id'],
"prob_home_win": float(row['prob_home_win']),
"prob_draw": float(row['prob_draw']),
"prob_away_win": float(row['prob_away_win']),
"manual_features_applied": applied,
"odds": odds_rows,
"weather": weather
})
return {
"predictions": results,
"skipped": skipped
}
def run_predictions():
data = get_predictions()
print("\nUpcoming Match Predictions:")
for p in data['predictions']:
print(f"Match {p['match_id']} | {p['home_team_id']} vs {p['away_team_id']} "
f"| 1: {p['prob_home_win']:.2f} X: {p['prob_draw']:.2f} 2: {p['prob_away_win']:.2f}")
if __name__ == "__main__":
run_predictions()