๐ฅ
Raw Data Collection
Downloaded 1,233 JSON files from Cricsheet.org โ the gold standard cricket data source used by professionals worldwide
๐
JSON to DataFrame
Parsed all JSON files using Python and converted into two structured Pandas DataFrames โ matches and ball-by-ball deliveries
๐
Team Name Merging
Merged renamed franchises โ Delhi Daredevils to Delhi Capitals, Kings XI Punjab to Punjab Kings
๐งน
Wicket Cleaning
Removed run outs from bowler wicket counts โ only credited legitimate dismissals to prevent incorrect rankings
๐
Phase Classification
Tagged every delivery with its phase โ Powerplay 1-6, Middle Overs 7-15 or Death Overs 16-20
โ
Result Filtering
Excluded No Result matches from win percentage calculations to ensure accurate statistics
Data Quality: Cricsheet.org data is verified and updated within days of each match.
This dataset covers every IPL match from 2008 to May 2026 โ more complete than any standard Kaggle dataset.
Toss Win Rate
51.6%
of toss winners also won the match
Coin Flip Rate
50.0%
expected if toss had zero impact
Bat First Win Rate
44.4%
when toss winner chose to bat
Field First Win Rate
53.8%
when toss winner chose to field
Chart 1A โ Toss Winner vs Match Winner
Out of 1214 completed matches โ how many did the toss winner actually win?
Chart 1B โ Bat First vs Field First Win Rate
Does the decision AFTER winning toss matter more than the toss itself?
Answer to Question 1: No โ toss winners win only 51.6% of matches โ
barely better than a coin flip at 50%. Teams choosing to FIELD FIRST win 53.8%
vs 44.4% for bat first. Dew factor in night matches makes bowling first smarter.
The toss decision matters more than winning the toss itself.
Powerplay Winners
1.374
runs per ball vs 1.220 for losers
Middle Overs Winners
1.365
runs per ball vs 1.206 for losers
Death Overs Winners
1.754
runs per ball vs 1.457 for losers
Most Decisive Phase
Death
biggest gap between winners and losers
Chart 2A โ Winners vs Losers Runs Per Ball by Phase
The gap between winning and losing teams is LARGEST in death overs
Chart 2B โ Average First Innings Score Evolution 2008 to 2026
IPL scores have risen 29 runs in 19 years โ driven by death over batting improvements
Answer to Question 2: Death Overs (16-20) are most linked to winning.
Winning teams score 1.754 runs per ball vs 1.457 for losing teams โ biggest gap of all phases.
Average scores rose from 161 in 2008 to 190 in 2026.
Teams that win the death overs battle win the match.
Top 5 IPL Batters โ All Time Runs (2008-2026)
| Rank | Batter | Total Runs | Note |
| 1st | V Kohli | 9,228 | All time leader |
| 2nd | RG Sharma | 7,331 | 2nd all time |
| 3rd | S Dhawan | 6,769 | 3rd all time |
| 4th | DA Warner | 6,567 | 4th all time |
| 5th | KL Rahul | 5,828 | 5th all time |
Top 5 IPL Bowlers โ All Time Wickets (2008-2026)
| Rank | Bowler | Wickets | Type |
| 1st | YS Chahal | 233 | Leg Spin |
| 2nd | B Kumar | 222 | Medium Fast |
| 3rd | SP Narine | 209 | Off Spin |
| 4th | PP Chawla | 192 | Leg Spin |
| 5th | JJ Bumrah | 190 | Fast |
Answer to Question 3:
Virat Kohli leads batting with 9,228 runs โ 1,897 ahead of Rohit Sharma.
YS Chahal leads bowling with 233 wickets โ remarkably a spinner not a pacer.
The top bowlers list is dominated by spinners proving mystery spin is IPL's most effective weapon.
One Finding That Surprised Us Most
A Spinner Has The Best Death Over Economy In IPL History!
Everyone assumes death overs belong to fast bowlers โ yorkers, bouncers, raw pace.
But Sunil Narine โ an OFF SPINNER โ has the BEST economy rate in death overs
across 19 IPL seasons at just 7.29 runs per over, beating every single pace bowler
including Malinga and Bumrah. Meanwhile Yuzvendra Chahal โ a LEG SPINNER โ
is the all time leading wicket taker with 233 wickets ahead of all feared pacers.
Analysing 293,308 deliveries proves that mystery spin beats raw pace in T20 cricket.
This completely overturns conventional T20 wisdom about death bowling.
7 Things IPL Data Taught Us
1
Toss is basically a coin flip โ conditions matter more
Winning the toss gives only 51.6% advantage. Smart captains who read conditions correctly win significantly more. The decision after the toss is what actually matters.
2
Virat Kohli is the greatest IPL batter of all time
9,228 runs across 19 seasons โ his consistency across all venues and conditions is unmatched by any other batter in IPL history.
3
Death overs decide matches โ not powerplay
The biggest gap between winning and losing teams is in overs 16-20. Teams that score more and concede less in death overs win matches consistently.
4
Spinners are IPL's most deadly weapon โ not pace
Chahal leads wickets with 233. Narine leads death economy at 7.29. Mystery spin consistently outperforms raw pace across all 19 seasons.
5
IPL scores have exploded โ 161 to 190 in 19 years
Average first innings score rose 29 runs over 19 seasons. New batting shots invented for T20 cricket have permanently changed how the game is played globally.
6
CSK are the greatest pressure team in IPL history
Chennai Super Kings have won the most playoff matches โ proving experience and calm leadership beats raw talent in knockout cricket.
7
Chepauk is a fortress for the team batting first
Only 35.4% of chases succeed at MA Chidambaram Stadium. Slow pitch and spin friendly surface make it nearly impossible to chase successfully.
# Step 1: Load all 1233 IPL JSON files from Cricsheet
import zipfile, json, pandas as pd
with zipfile.ZipFile('ipl_male_json.zip') as z:
for filename in z.namelist():
data = json.loads(z.read(filename))
# Step 2: Merge renamed franchises
matches['winner'] = matches['winner'].replace({
'Delhi Daredevils': 'Delhi Capitals',
'Kings XI Punjab': 'Punjab Kings'
})
# Step 3: Toss analysis
toss_won = matches[matches['toss_winner'] == matches['winner']]
toss_pct = round(len(toss_won) / len(matches) * 100, 1)
# Step 4: Phase analysis โ winners vs losers
deliveries['phase'] = deliveries['over'].apply(get_phase)
phase_runs = deliveries.groupby(['phase','won'])['total_runs'].mean()
# Step 5: Top 5 batters and bowlers
top5_bat = deliveries.groupby('batter')['batsman_runs'].sum().nlargest(5)
top5_bowl = wickets.groupby('bowler')['dismissed_player'].count().nlargest(5)