Spaces:
Sleeping
Sleeping
utils/__pycache__
Browse files
utils/__pycache__/__init__.cpython-314.pyc
DELETED
|
Binary file (163 Bytes)
|
|
|
utils/__pycache__/encoders.cpython-314.pyc
DELETED
|
Binary file (3.13 kB)
|
|
|
utils/__pycache__/predictor.cpython-314.pyc
DELETED
|
Binary file (8.86 kB)
|
|
|
utils/encoders.py
DELETED
|
@@ -1,72 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
utils/encoders.py
|
| 3 |
-
Label-encoding maps that mirror the sklearn LabelEncoder used during training.
|
| 4 |
-
All lists are sorted alphabetically – matching the default LabelEncoder order.
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
# ── IPL Teams (alphabetical order = encoding index) ──────────────────────────
|
| 8 |
-
TEAMS = sorted([
|
| 9 |
-
"Chennai Super Kings",
|
| 10 |
-
"Delhi Capitals",
|
| 11 |
-
"Gujarat Titans",
|
| 12 |
-
"Kolkata Knight Riders",
|
| 13 |
-
"Lucknow Super Giants",
|
| 14 |
-
"Mumbai Indians",
|
| 15 |
-
"Punjab Kings",
|
| 16 |
-
"Rajasthan Royals",
|
| 17 |
-
"Royal Challengers Bangalore",
|
| 18 |
-
"Sunrisers Hyderabad",
|
| 19 |
-
])
|
| 20 |
-
|
| 21 |
-
TEAM_ENC: dict[str, int] = {team: idx for idx, team in enumerate(TEAMS)}
|
| 22 |
-
|
| 23 |
-
# ── IPL Venues (alphabetical order = encoding index) ─────────────────────────
|
| 24 |
-
VENUES = sorted([
|
| 25 |
-
"Arun Jaitley Stadium",
|
| 26 |
-
"Brabourne Stadium",
|
| 27 |
-
"DY Patil Stadium",
|
| 28 |
-
"Eden Gardens",
|
| 29 |
-
"Feroz Shah Kotla",
|
| 30 |
-
"MA Chidambaram Stadium",
|
| 31 |
-
"MCA Stadium",
|
| 32 |
-
"Maharashtra Cricket Association Stadium",
|
| 33 |
-
"Narendra Modi Stadium",
|
| 34 |
-
"Punjab Cricket Association Stadium",
|
| 35 |
-
"Rajiv Gandhi International Stadium",
|
| 36 |
-
"Sawai Mansingh Stadium",
|
| 37 |
-
"Wankhede Stadium",
|
| 38 |
-
])
|
| 39 |
-
|
| 40 |
-
VENUE_ENC: dict[str, int] = {venue: idx for idx, venue in enumerate(VENUES)}
|
| 41 |
-
|
| 42 |
-
# ── Phase encoding ────────────────────────────────────────────────────────────
|
| 43 |
-
PHASE_LABELS = {
|
| 44 |
-
0: "Powerplay (Ov 1–6)",
|
| 45 |
-
1: "Middle Overs (Ov 7–15)",
|
| 46 |
-
2: "Death Overs (Ov 16–20)",
|
| 47 |
-
}
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
def encode_team(name: str) -> int:
|
| 51 |
-
"""Return integer encoding for a team name. Defaults to 0 if not found."""
|
| 52 |
-
return TEAM_ENC.get(name, 0)
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
def encode_venue(name: str) -> int:
|
| 56 |
-
"""Return integer encoding for a venue name. Defaults to 0 if not found."""
|
| 57 |
-
return VENUE_ENC.get(name, 0)
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
def get_phase(over: int) -> int:
|
| 61 |
-
"""
|
| 62 |
-
Returns match phase (0/1/2) based on over number.
|
| 63 |
-
0 = Powerplay (overs 1–6)
|
| 64 |
-
1 = Middle overs(overs 7–15)
|
| 65 |
-
2 = Death overs (overs 16–20)
|
| 66 |
-
"""
|
| 67 |
-
if over < 6:
|
| 68 |
-
return 0
|
| 69 |
-
elif over < 15:
|
| 70 |
-
return 1
|
| 71 |
-
else:
|
| 72 |
-
return 2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils/predictor.py
DELETED
|
@@ -1,161 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
utils/predictor.py
|
| 3 |
-
Loads the four XGBoost models and exposes a single predict() method
|
| 4 |
-
that accepts a raw request dict and returns a structured result dict.
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
import os
|
| 8 |
-
import pickle
|
| 9 |
-
import warnings
|
| 10 |
-
import pandas as pd
|
| 11 |
-
|
| 12 |
-
from utils.encoders import encode_team, encode_venue, get_phase, PHASE_LABELS
|
| 13 |
-
|
| 14 |
-
warnings.filterwarnings("ignore")
|
| 15 |
-
|
| 16 |
-
# ── Model file paths ──────────────────────────────────────────────────────────
|
| 17 |
-
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| 18 |
-
MODELS_DIR = os.path.join(BASE_DIR, "models")
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
class CricketPredictor:
|
| 22 |
-
"""
|
| 23 |
-
Loads all four pre-trained XGBoost models and provides
|
| 24 |
-
ball-level and match-level predictions.
|
| 25 |
-
|
| 26 |
-
Models
|
| 27 |
-
------
|
| 28 |
-
dot_model : DotBall.pkl – P(dot ball) binary
|
| 29 |
-
boundary_model : BoundaryModel.pkl – P(boundary 4/6) binary
|
| 30 |
-
run_model : RunPrediction.pkl – P(0..5 runs) multi-class
|
| 31 |
-
win_model : IPLchasingTeamWin.pkl– P(chase success) binary
|
| 32 |
-
"""
|
| 33 |
-
|
| 34 |
-
def __init__(self):
|
| 35 |
-
print("Loading models...")
|
| 36 |
-
self.dot_model = self._load("DotBall.pkl")
|
| 37 |
-
self.boundary_model = self._load("BoundaryModel.pkl")
|
| 38 |
-
self.run_model = self._load("RunPrediction.pkl")
|
| 39 |
-
self.win_model = self._load("IPLchasingTeamWin.pkl")
|
| 40 |
-
print("✓ All 4 models loaded successfully.\n")
|
| 41 |
-
|
| 42 |
-
# ── Public API ────────────────────────────────────────────────────────────
|
| 43 |
-
|
| 44 |
-
def predict(self, data: dict) -> dict:
|
| 45 |
-
"""
|
| 46 |
-
Main prediction entry point.
|
| 47 |
-
|
| 48 |
-
Parameters
|
| 49 |
-
----------
|
| 50 |
-
data : dict Raw JSON body from the API request.
|
| 51 |
-
|
| 52 |
-
Returns
|
| 53 |
-
-------
|
| 54 |
-
dict with keys:
|
| 55 |
-
dot_ball_prob float – % probability of a dot ball
|
| 56 |
-
boundary_prob float – % probability of a 4 or 6
|
| 57 |
-
expected_runs float – weighted average expected runs
|
| 58 |
-
run_distribution list – [P(0), P(1), P(2), P(3), P(4), P(5)]
|
| 59 |
-
win_probability float|None – % win prob for chasing team (innings=2 only)
|
| 60 |
-
phase str – human-readable phase label
|
| 61 |
-
"""
|
| 62 |
-
# ── 1. Parse & validate inputs ────────────────────────────────────────
|
| 63 |
-
batting_team = str(data.get("batting_team", ""))
|
| 64 |
-
bowling_team = str(data.get("bowling_team", ""))
|
| 65 |
-
venue = str(data.get("venue", ""))
|
| 66 |
-
innings = int(data.get("innings", 1))
|
| 67 |
-
over = int(data.get("over", 1))
|
| 68 |
-
ball_in_over = int(data.get("ball_in_over", 1))
|
| 69 |
-
current_score = float(data.get("current_score", 0))
|
| 70 |
-
wickets_fallen = int(data.get("wickets_fallen", 0))
|
| 71 |
-
|
| 72 |
-
# Optional performance features
|
| 73 |
-
batter_sr = float(data.get("batter_sr", 130))
|
| 74 |
-
bowler_eco = float(data.get("bowler_eco", 7.5))
|
| 75 |
-
last_6_runs = float(data.get("last_6_runs", 6))
|
| 76 |
-
last_12_runs = float(data.get("last_12_runs", 12))
|
| 77 |
-
prev_runs = float(data.get("prev_runs", 1))
|
| 78 |
-
prev_wicket = int(data.get("prev_wicket", 0))
|
| 79 |
-
last_6_wickets = int(data.get("last_6_wickets", 0))
|
| 80 |
-
striker_enc = int(data.get("striker_enc", 0))
|
| 81 |
-
bowler_enc = int(data.get("bowler_enc", 0))
|
| 82 |
-
|
| 83 |
-
# ── 2. Derived features ───────────────────────────────────────────────
|
| 84 |
-
balls_bowled = over * 6 + ball_in_over
|
| 85 |
-
run_rate = round(current_score / max(balls_bowled, 1) * 6, 3)
|
| 86 |
-
phase = get_phase(over)
|
| 87 |
-
|
| 88 |
-
# ── 3. Build ball-level feature DataFrame ─────────────────────────────
|
| 89 |
-
ball_df = pd.DataFrame([{
|
| 90 |
-
"striker_enc" : striker_enc,
|
| 91 |
-
"bowler_enc" : bowler_enc,
|
| 92 |
-
"batting_team_enc" : encode_team(batting_team),
|
| 93 |
-
"bowling_team_enc" : encode_team(bowling_team),
|
| 94 |
-
"venue_enc" : encode_venue(venue),
|
| 95 |
-
"over" : over,
|
| 96 |
-
"ball_in_over" : ball_in_over,
|
| 97 |
-
"phase" : phase,
|
| 98 |
-
"current_score" : current_score,
|
| 99 |
-
"wickets_fallen" : wickets_fallen,
|
| 100 |
-
"run_rate" : run_rate,
|
| 101 |
-
"prev_runs" : prev_runs,
|
| 102 |
-
"prev_wicket" : prev_wicket,
|
| 103 |
-
"last_6_runs" : last_6_runs,
|
| 104 |
-
"last_12_runs" : last_12_runs,
|
| 105 |
-
"last_6_wickets" : last_6_wickets,
|
| 106 |
-
"batter_sr" : batter_sr,
|
| 107 |
-
"bowler_eco" : bowler_eco,
|
| 108 |
-
}])
|
| 109 |
-
|
| 110 |
-
# ── 4. Run the three ball-level models ────────────────────────────────
|
| 111 |
-
dot_prob = float(self.dot_model.predict_proba(ball_df)[0][1])
|
| 112 |
-
boundary_prob = float(self.boundary_model.predict_proba(ball_df)[0][1])
|
| 113 |
-
|
| 114 |
-
run_proba = self.run_model.predict_proba(ball_df)[0]
|
| 115 |
-
run_dist = [round(float(p), 4) for p in run_proba]
|
| 116 |
-
expected_runs = round(sum(i * run_dist[i] for i in range(len(run_dist))), 3)
|
| 117 |
-
|
| 118 |
-
# ── 5. Win probability (2nd innings only) ─────────────────────────────
|
| 119 |
-
win_prob = None
|
| 120 |
-
if innings == 2:
|
| 121 |
-
balls_remaining = int(data.get("balls_remaining", 60))
|
| 122 |
-
balls_done_chase = max(120 - balls_remaining, 1)
|
| 123 |
-
chase_rr = round(current_score / balls_done_chase * 6, 3)
|
| 124 |
-
|
| 125 |
-
win_df = pd.DataFrame([{
|
| 126 |
-
"batting_team" : encode_team(batting_team),
|
| 127 |
-
"bowling_team" : encode_team(bowling_team),
|
| 128 |
-
"venue" : encode_venue(venue),
|
| 129 |
-
"innings" : innings,
|
| 130 |
-
"current_score" : current_score,
|
| 131 |
-
"wickets_fallen" : wickets_fallen,
|
| 132 |
-
"balls_remaining": balls_remaining,
|
| 133 |
-
"run_rate" : chase_rr,
|
| 134 |
-
}])
|
| 135 |
-
win_prob = round(float(self.win_model.predict_proba(win_df)[0][1]) * 100, 1)
|
| 136 |
-
|
| 137 |
-
# ── 6. Return structured result ───────────────────────────────────────
|
| 138 |
-
return {
|
| 139 |
-
"dot_ball_prob" : round(dot_prob * 100, 1),
|
| 140 |
-
"boundary_prob" : round(boundary_prob * 100, 1),
|
| 141 |
-
"expected_runs" : expected_runs,
|
| 142 |
-
"run_distribution": run_dist,
|
| 143 |
-
"win_probability" : win_prob,
|
| 144 |
-
"phase" : PHASE_LABELS[phase],
|
| 145 |
-
"run_rate" : round(run_rate, 2),
|
| 146 |
-
}
|
| 147 |
-
|
| 148 |
-
def models_loaded(self) -> list[str]:
|
| 149 |
-
return ["DotBall", "BoundaryModel", "RunPrediction", "IPLchasingTeamWin"]
|
| 150 |
-
|
| 151 |
-
# ── Private helpers ───────────────────────────────────────────────────────
|
| 152 |
-
|
| 153 |
-
def _load(self, filename: str):
|
| 154 |
-
path = os.path.join(MODELS_DIR, filename)
|
| 155 |
-
if not os.path.exists(path):
|
| 156 |
-
raise FileNotFoundError(
|
| 157 |
-
f"Model file not found: {path}\n"
|
| 158 |
-
f"Make sure {filename} is inside the 'models/' folder."
|
| 159 |
-
)
|
| 160 |
-
with open(path, "rb") as f:
|
| 161 |
-
return pickle.load(f)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|