File size: 7,729 Bytes
fad7dff
 
 
 
 
 
 
1c7a005
fad7dff
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1c7a005
fad7dff
 
 
 
1c7a005
fad7dff
1c7a005
 
fad7dff
1c7a005
 
fad7dff
1c7a005
 
fad7dff
 
 
 
 
1c7a005
fad7dff
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import pandas as pd
import numpy as np

def build_batting_features(deliveries: pd.DataFrame) -> pd.DataFrame:
    
    d = deliveries[deliveries["is_wide"] == 0].copy()
    
    agg = d.groupby("batsman").agg(
        total_runs = ("batsman_runs", "sum"),
        balls_faced = ("is_legal_delivery", "sum"),
        matches_batted = ("match_id", "nunique"),
        innings = ("inning", "count"),
        fours = ("is_four", "sum"),
        sixes = ("is_six", "sum"),
        dot_balls = ("is_dot_ball", "sum"),
        dismissals = ("is_wicket", "sum"),
    ).reset_index()
    
    agg["strike_rate"] = (agg["total_runs"] / agg["balls_faced"].replace(0, np.nan) * 100).round(2)
    agg["batting_average"] = (agg["total_runs"] / agg["dismissals"].replace(0, np.nan)).round(2)
    agg["boundary_rate"] = ((agg["fours"] + agg["sixes"]) / agg["balls_faced"].replace(0, np.nan) * 100).round(2)
    agg["dot_ball_pct"] = (agg["dot_balls"] / agg["balls_faced"].replace(0, np.nan) * 100).round(2)
    agg["runs_per_match"] = (agg["total_runs"] / agg["matches_batted"].replace(0, np.nan)).round(2)
    
    for phase in ["powerplay", "middle", "death"]:
        phase_df = d[d["over_phase"] == phase].groupby("batsman").agg(
            _runs = ("batsman_runs", "sum"),
            _balls = ("is_legal_delivery", "sum"),
        ).reset_index()
        phase_df[f"sr_{phase}"] = (phase_df["_runs"] / phase_df["_balls"].replace(0, np.nan) * 100).round(2)
        agg = agg.merge(phase_df[["batsman", f"sr_{phase}"]], on="batsman", how="left")
        
    inning_scores = d.groupby(["batsman", "match_id", "inning"])["batsman_runs"].sum().reset_index()
    inning_scores.columns = ["batsman", "match_id", "inning", "innings_runs"]
    
    fifties = inning_scores[inning_scores["innings_runs"].between(50, 99)].groupby("batsman").size().rename("fifties")
    hundreds = inning_scores[inning_scores["innings_runs"] >= 100].groupby("batsman").size().rename("hundreds")
    
    agg = agg.merge(fifties, on="batsman", how="left")
    agg = agg.merge(hundreds, on="batsman", how="left")
    agg["fifties"] = agg["fifties"].fillna(0).astype(int)
    agg["hundreds"] = agg["hundreds"].fillna(0).astype(int)
    
    agg = agg[agg["balls_faced"] >= 30].copy()
    
    print(f"build_batting_features: {len(agg)} batsmans with 30+ balls faced")
    return agg.sort_values("total_runs", ascending=False).reset_index(drop=True)


def build_bowling_features(deliveries: pd.DataFrame) -> pd.DataFrame:
    d = deliveries.copy()
    
    agg = d.groupby("bowler").agg(
        balls_bowled = ("is_legal_delivery", "sum"),
        runs_conceded = ("total_runs", "sum"),
        wickets = ("is_wicket", "sum"),
        dot_balls = ("is_dot_ball", "sum"),
        wides = ("is_wide", "sum"),
        no_balls = ("is_noball", "sum"),
        matches_bowled = ("match_id", "nunique"),
        fours_conceded = ("is_four", "sum"),
        sixes_conceded = ("is_six", "sum"),
    ).reset_index()
    
    overs = agg["balls_bowled"] / 6
    agg["economy_rate"] = (agg["runs_conceded"] / overs.replace(0, np.nan)).round(2)
    agg["bowling_average"] = (agg["runs_conceded"] / agg["wickets"].replace(0, np.nan)).round(2)
    agg["bowling_sr"] = (agg["balls_bowled"] / agg["wickets"].replace(0, np.nan)).round(2)
    agg["dot_ball_pct"] = (agg["dot_balls"] / agg["balls_bowled"].replace(0, np.nan) * 100).round(2)
    agg["wickets_per_match"] = (agg["wickets"] / agg["matches_bowled"].replace(0, np.nan)).round(2)
    
    for phase in  ["powerplay", "middle", 'death']:
        phase_df = d[d["over_phase"] == phase].groupby("bowler").agg(
            _runs = ("total_runs", "sum"),
            _balls = ("is_legal_delivery", "sum"),
        ).reset_index()
        phase_df[f"economy_{phase}"] = (phase_df["_runs"] / (phase_df["_balls"] / 6).replace(0, np.nan)).round(2)
        agg = agg.merge(phase_df[["bowler", f"economy_{phase}"]], on="bowler", how="left")
        
    inning_wkt = d.groupby(["bowler", "match_id", "inning"])["is_wicket"].sum().reset_index()
    inning_wkt.columns = ["bowler", "match_id", "inning", "wickets_in_inning"]
        
    three_wkt = inning_wkt[inning_wkt["wickets_in_inning"] >= 3].groupby("bowler").size().rename("three_wicket_haul")
    five_wkt = inning_wkt[inning_wkt["wickets_in_inning"] >= 5].groupby("bowler").size().rename("five_wicket_haul")
        
    agg = agg.merge(three_wkt, on="bowler", how="left")
    agg = agg.merge(five_wkt, on="bowler", how="left")
    agg["three_wicket_haul"] = agg["three_wicket_haul"].fillna(0).astype(int)
    agg["five_wicket_haul"] = agg["five_wicket_haul"].fillna(0).astype(int)
        
    agg = agg[agg["balls_bowled"] >= 60].copy()
        
    print(f"build_bowling_features: {len(agg)} bowlers with 60+ balls bowled")
    return agg.sort_values("wickets", ascending=False).reset_index(drop=True)
    
def build_match_summary(matches: pd.DataFrame, deliveries: pd.DataFrame) -> pd.DataFrame:
    
    innings_total = deliveries.groupby(["match_id", "inning", "batting_team"]).agg(
        total_runs = ("total_runs", "sum"),
        total_wickets = ("is_wicket", "sum"),
        total_balls = ("is_legal_delivery", "sum"),
        total_fours = ("is_four", "sum"),
        total_sixes = ("is_six", "sum"),
        dot_balls = ("is_dot_ball", "sum"),
    ).reset_index()
    
    innings_total["run_rate"] = (
        innings_total["total_runs"] / (innings_total["total_balls"] / 6).replace(0, np.nan)
    ).round(2)
    
    inn1 = innings_total[innings_total["inning"] == 1].add_prefix("inn1_").rename(columns={"inn1_match_id": "match_id"})
    inn2 = innings_total[innings_total["inning"] == 2].add_prefix("inn2_").rename(columns={"inn2_match_id": "match_id"})
    
    summary = matches.merge(inn1.drop(columns=["inn1_inning"]), on="match_id", how="left")
    summary = summary.merge(inn2.drop(columns=["inn2_inning"]), on="match_id", how="left")
    
    summary["toss_winner_won"] = (summary["toss_winner"] == summary["winner"]).astype(int)
    summary["batting_first_team"] = summary["inn1_batting_team"]
    
    summary["batting_first_won"] = (
        summary["inn1_batting_team"] == summary["winner"]
    ).astype(int)
    
    summary["score_diff"] = summary["inn1_total_runs"] - summary["inn2_total_runs"]
    
    min_season = summary["season"].min()
    summary["season_num"] = summary["season"] - min_season + 1
    
    print(f"build_match_summary: {summary.shape}")
    return summary.reset_index(drop=True)

def build_team_season_stats(matches: pd.DataFrame) -> pd.DataFrame:
    
    records = []
    for season in sorted(matches["season"].unique()):
        season_df = matches[matches["season"] == season]
        all_teams = pd.concat([season_df["team1"], season_df["team2"]]).unique()
        
        for team in all_teams:
            played = season_df[(season_df["team1"] == team) | (season_df["team2"] == team)]
            won = season_df[season_df["winner"] == team]
            
            bat_first = played[played["inn1_batting_team"] == team] if "inn1_batting_team" in played.columns else pd.DataFrame()
            toss_won = played[played["toss_winner"] == team]
            toss_bat = toss_won[toss_won["toss_decision"] == "bat"]
            
            records.append({
                "season": season,
                "team": team,
                "matches_played": len(played),
                "matches_won": len(won),
                "win_pct": round(len(won) / len(played) * 100, 1) if len(played) > 0 else 0,
                "toss_wins": len(toss_won),
                "toss_bat_choice": len(toss_bat),
            })
            
    df = pd.DataFrame(records)
    print(f"build_team_season_stats: {df.shape}")
    return df