Data Cleaning — IPL Sports Analytics¶
Setup¶
import pandas as pd
import numpy as np
matches = pd.read_csv("matches.csv")
deliveries = pd.read_csv("deliveries.csv")
print(f"Matches: {matches.shape}, Deliveries: {deliveries.shape}")
print(f"\nMatches missing:\n{matches.isnull().sum()[matches.isnull().sum() > 0]}")
print(f"\nDeliveries missing:\n{deliveries.isnull().sum()[deliveries.isnull().sum() > 0]}")
Step 1 — Standardise Team Names¶
Teams renamed over the years. Map old names to current ones for consistent multi-season analysis.
team_name_map = {
"Delhi Daredevils": "Delhi Capitals",
"Kings XI Punjab": "Punjab Kings",
"Rising Pune Supergiant": "Rising Pune Supergiants",
"Deccan Chargers": "Sunrisers Hyderabad", # franchise lineage (optional)
}
for col in ["team1", "team2", "toss_winner", "winner"]:
matches[col] = matches[col].replace(team_name_map)
for col in ["batting_team", "bowling_team"]:
deliveries[col] = deliveries[col].replace(team_name_map)
Step 2 — Handle Match Results with No Winner¶
# Matches with no result (rain, tie) have NULL winner
print(f"Matches with no winner: {matches['winner'].isna().sum()}")
# For win-rate analysis, exclude no-result matches
matches_with_result = matches[matches["winner"].notna()].copy()
Step 3 — Parse Dates¶
matches["date"] = pd.to_datetime(matches["date"], errors="coerce")
matches["year"] = matches["date"].dt.year
Step 4 — Create Batting Analysis Base¶
The key insight: exclude wides and no-balls from "balls faced."
# A ball "faced" by the batsman excludes wides and no-balls
deliveries["is_ball_faced"] = ~deliveries["extras_type"].isin(["wides", "noballs"])
deliveries["is_ball_faced"] = deliveries["is_ball_faced"].astype(int)
# Legal deliveries for bowler (wides and no-balls don't count as a ball bowled)
deliveries["is_legal_ball"] = ~deliveries["extras_type"].isin(["wides", "noballs"])
deliveries["is_legal_ball"] = deliveries["is_legal_ball"].astype(int)
# Boundary flags
deliveries["is_four"] = (deliveries["batsman_runs"] == 4).astype(int)
deliveries["is_six"] = (deliveries["batsman_runs"] == 6).astype(int)
Step 5 — Add Phase of Innings¶
def innings_phase(over):
if over < 6:
return "Powerplay (1-6)"
elif over < 15:
return "Middle (7-15)"
else:
return "Death (16-20)"
deliveries["phase"] = deliveries["over"].apply(innings_phase)
Step 6 — Build a Batsman Summary Table¶
batsman_stats = (
deliveries.groupby("batter")
.agg(
runs=("batsman_runs", "sum"),
balls_faced=("is_ball_faced", "sum"),
fours=("is_four", "sum"),
sixes=("is_six", "sum"),
innings=("match_id", "nunique"),
)
.reset_index()
)
# Strike rate (runs per 100 balls)
batsman_stats["strike_rate"] = (batsman_stats["runs"] / batsman_stats["balls_faced"] * 100).round(1)
# Filter to meaningful sample size
batsman_stats = batsman_stats[batsman_stats["balls_faced"] >= 200]
print(batsman_stats.sort_values("runs", ascending=False).head(10))
Step 7 — Build a Bowler Summary Table¶
# Wickets credited to bowler (exclude run outs — not the bowler's wicket)
bowler_wicket_kinds = ["bowled", "caught", "lbw", "stumped", "caught and bowled", "hit wicket"]
deliveries["is_bowler_wicket"] = (
deliveries["is_wicket"] &
deliveries["dismissal_kind"].isin(bowler_wicket_kinds)
).astype(int)
# Runs conceded by bowler = batsman_runs + wides + noballs (NOT byes/leg-byes)
deliveries["bowler_runs"] = deliveries["batsman_runs"] + deliveries["extra_runs"].where(
deliveries["extras_type"].isin(["wides", "noballs"]), 0
)
bowler_stats = (
deliveries.groupby("bowler")
.agg(
runs_conceded=("bowler_runs", "sum"),
balls_bowled=("is_legal_ball", "sum"),
wickets=("is_bowler_wicket", "sum"),
)
.reset_index()
)
bowler_stats["overs"] = bowler_stats["balls_bowled"] / 6
bowler_stats["economy"] = (bowler_stats["runs_conceded"] / bowler_stats["overs"]).round(2)
bowler_stats["bowling_sr"] = (bowler_stats["balls_bowled"] / bowler_stats["wickets"]).round(1)
bowler_stats = bowler_stats[bowler_stats["balls_bowled"] >= 500]
print(bowler_stats.sort_values("wickets", ascending=False).head(10))
Step 8 — Export¶
matches.to_csv("matches_clean.csv", index=False)
deliveries.to_csv("deliveries_clean.csv", index=False)
batsman_stats.to_csv("batsman_stats.csv", index=False)
bowler_stats.to_csv("bowler_stats.csv", index=False)
print("Cleaned files exported.")