ministerchief commited on
Commit
d883c0e
Β·
verified Β·
1 Parent(s): b4d26c2

Upload 2 files

Browse files
Files changed (2) hide show
  1. utils/encoders.py +72 -0
  2. utils/predictor.py +161 -0
utils/encoders.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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)