| """
|
| live_predict.py
|
| ---------------
|
| Re-runs the win + podium models each lap using in-race position data
|
| to update predictions as the race evolves.
|
| """
|
|
|
| import pandas as pd
|
| from f1predictor.train import load_models, FEATURES
|
| from f1predictor.features import get_driver_encoding
|
|
|
|
|
| def predict_for_lap(
|
| lap_positions: dict,
|
| all_lap_history: dict,
|
| current_lap: int,
|
| track_temp: float = 30.0,
|
| is_wet: bool = False,
|
| features_path: str = "data/features.csv",
|
| ) -> dict:
|
| """
|
| Re-predict win and podium probabilities using in-race rolling form.
|
|
|
| Instead of historical season averages, uses the last 3 laps'
|
| positions from the current race as the rolling form signal.
|
|
|
| Returns:
|
| {driver: {win_prob: float, podium_prob: float}}
|
| """
|
| podium_model, win_model = load_models()
|
| driver_encoding = get_driver_encoding(features_path)
|
|
|
| rows = []
|
| for driver, data in lap_positions.items():
|
|
|
| recent_positions = []
|
| for past_lap in range(max(1, current_lap - 3), current_lap):
|
| past_data = all_lap_history.get(past_lap, {}).get(driver)
|
| if past_data:
|
| recent_positions.append(past_data["position"])
|
|
|
| avg_recent = sum(recent_positions) / len(recent_positions) if recent_positions else 10.0
|
|
|
| rows.append({
|
| "driver": driver,
|
| "grid_pos": data.get("position", 10),
|
| "quali_pos": data.get("position", 10),
|
| "avg_finish_last3": avg_recent,
|
| "avg_quali_last3": avg_recent,
|
| "track_temp": track_temp,
|
| "is_wet": int(is_wet),
|
| "driver_id": driver_encoding.get(driver, -1),
|
| })
|
|
|
| df = pd.DataFrame(rows)
|
| X = df[FEATURES]
|
|
|
| win_probs = win_model.predict_proba(X)[:, 1] * 100
|
| podium_probs = podium_model.predict_proba(X)[:, 1] * 100
|
|
|
| return {
|
| row["driver"]: {
|
| "win_prob": round(float(win_probs[i]), 1),
|
| "podium_prob": round(float(podium_probs[i]), 1),
|
| }
|
| for i, row in enumerate(rows)
|
| } |