Project: IPL Match Analysis
A mini-project inspired by notebook 06: load IPL match results, convert dates, and answer real questions — most wins, win %, toss decisions, wins by season, champions and top venues.
Notebook 06 of the course analyses the real IPL 2008–2020 match file (816 matches, 17 columns). Here we rebuild that analysis on a small 18-match sample (2018–2020) built inline, so every step runs in your browser. The columns match the real dataset, so the exact same code works on the full file.
text
date team1 team2 toss_winner toss_decision winner
0 2018-04-07 MI CSK CSK field CSK
1 2018-04-08 KKR RCB KKR field KKR
2 2018-04-10 CSK KKR CSK field CSK
3 2018-04-21 RCB DC RCB field RCB
4 2018-05-05 SRH DC DC bat SRHDefinition
A mini-project applies the EDA workflow to one dataset to answer a fixed set of business questions. Here the dataset is one row per IPL match, with the teams (
team1,team2), the toss (toss_winner,toss_decision=bat/field), the result (winner,result= won byruns/wickets,margin), thevenue/city, and theplayer_of_match.
Our questions:
- Which team won the most matches — and the highest percentage of matches played?
- What do captains choose after winning the toss, and does winning the toss help?
- How did each team do season by season, and who won each final?
- Which venues hosted the most matches? What were the biggest wins?
Syntax — tools used in this project
python
pd.read_csv(io.StringIO(text)) # load (a real file path works the same)
pd.to_datetime(s) · s.dt.year · s.dt.month_name()
s.value_counts(normalize=False) # counts (or proportions)
s1.add(s2, fill_value=0) # add two count Series safely
df.groupby(col)[col2].agg([...]) # per-group stats
df.pivot_table(index=, columns=, values=, aggfunc="count", fill_value=0)
pd.crosstab(row_series, col_series) # frequency table
df.sort_values(...).groupby(...).last() # last row per group (the final)| Function / parameter | Meaning | Default |
|---|---|---|
value_counts(normalize=) | True → proportions that sum to 1 instead of counts | False |
Series.add(other, fill_value=) | adds aligned on index; missing labels treated as fill_value | None (→ NaN) |
pivot_table(aggfunc=) | how to combine values in each cell ("count", "sum", "mean"…) | "mean" |
pivot_table(fill_value=) | value for empty cells | None |
pd.crosstab(index, columns) | counts how often each combination occurs | — |
Step 1 — load and fix the date type
python
import pandas as pd
import io
ipl = pd.read_csv(io.StringIO(IPL_CSV)) # the 18-match CSV text (see the examples)
print(ipl.shape)
ipl["date"] = pd.to_datetime(ipl["date"])
ipl["season"] = ipl["date"].dt.year
print(ipl[["date", "season"]].dtypes)text
(18, 11)
date datetime64[us]
season int32
dtype: objectHow it works: read_csv reads dates as plain text (str). Converting with pd.to_datetime unlocks the .dt accessor, so .dt.year gives the season — exactly what notebook 06 does with ipl3['year'] = ipl3['date'].dt.year.
Step 2 — most wins and win percentage
python
print(ipl["winner"].value_counts())
played = ipl["team1"].value_counts().add(ipl["team2"].value_counts(), fill_value=0)
summary = pd.DataFrame({"played": played, "won": ipl["winner"].value_counts()}).fillna(0).astype(int)
summary["win_pct"] = (summary["won"] / summary["played"] * 100).round(1)
print(summary.sort_values("win_pct", ascending=False))text
winner
CSK 5
MI 5
KKR 3
RCB 2
SRH 2
DC 1
Name: count, dtype: int64
played won win_pct
CSK 6 5 83.3
MI 8 5 62.5
SRH 4 2 50.0
KKR 6 3 50.0
RCB 6 2 33.3
DC 6 1 16.7How it works: a team can appear in either team1 or team2, so matches played = count in team1 + count in team2 (the notebook does the same with +). .add(..., fill_value=0) is safer than +: a team that only ever appears in one column would get NaN with +. CSK and MI both won 5, but CSK did it in 6 matches (83.3%) — raw counts can mislead; percentages are fairer.
Step 3 — the toss
python
print(ipl["toss_decision"].value_counts(normalize=True).round(2))
ipl["toss_win_match_win"] = ipl["toss_winner"] == ipl["winner"]
print(ipl["toss_win_match_win"].mean().round(2))
print(ipl.groupby("toss_decision")["toss_win_match_win"].agg(["count", "mean"]).round(2))text
toss_decision
field 0.83
bat 0.17
Name: proportion, dtype: float64
0.5
count mean
toss_decision
bat 3 0.33
field 15 0.53How it works: 83% of toss winners chose to field first. Comparing two columns gives a boolean Series, and the mean of booleans is the proportion of True — toss winners won exactly 50% of matches, so in this sample the toss gave no overall advantage. Choosing to field converted a bit better (53% vs 33%), but with only 3 "bat" matches that difference is not reliable — always check the count before trusting a percentage.
Step 4 — season by season, and the champions
python
print(ipl.pivot_table(index="winner", columns="season", values="venue",
aggfunc="count", fill_value=0))
champions = ipl.sort_values("date").groupby("season").last()[["winner", "venue"]]
print(champions)text
season 2018 2019 2020
winner
CSK 3 1 1
DC 0 1 0
KKR 1 1 1
MI 0 2 3
RCB 1 0 1
SRH 1 1 0
winner venue
season
2018 CSK Wankhede Stadium
2019 MI Rajiv Gandhi Stadium
2020 MI Dubai International StadiumHow it works: pivot_table with aggfunc="count" counts rows for every (winner, season) pair — any non-null column works as values. fill_value=0 replaces "no wins" NaN with 0. For champions, the final is the last match of each season: sort by date, group by season, take .last() — the same idea as ipl4.groupby('year').last() in notebook 06. (Our sample is real-history friendly: CSK 2018, MI 2019 and 2020.)
Step 5 — venues and biggest wins
python
print(ipl["venue"].value_counts().head(3))
print(ipl.groupby("result")["margin"].agg(["count", "mean", "max"]).round(1))
print(ipl.loc[ipl["result"] == "runs"].nlargest(3, "margin")[["date", "winner", "team1", "team2", "margin"]])text
venue
Dubai International Stadium 4
Wankhede Stadium 3
M Chinnaswamy Stadium 3
Name: count, dtype: int64
count mean max
result
runs 7 31.3 59
wickets 11 5.6 9
date winner team1 team2 margin
15 2020-10-24 KKR KKR DC 59
16 2020-11-05 MI MI DC 57
14 2020-09-23 MI KKR MI 49How it works: a margin in runs and a margin in wickets are different units, so we group by result before averaging — mixing them would be meaningless. Teams batting first won 7 times (by 31.3 runs on average); chasing teams won 11 times.
Findings
| Question | Answer (18-match sample) |
|---|---|
| Most wins | CSK and MI, 5 each — CSK has the best win % (83.3%) |
| Toss choice | 83% chose to field |
| Does the toss help? | Toss winners won 50% — no clear advantage |
| Champions | CSK (2018), MI (2019, 2020) |
| Busiest venue | Dubai International Stadium (4 matches) |
| Biggest win | KKR by 59 runs vs DC (2020-10-24) |
Note (real data): the full 2008–2020 file needs extra cleaning that notebook 06 shows:
fillna({"method": "fare_match"})beforedropna(), merging renamed teams withreplace({"Delhi Daredevils": "Delhi Capitals", ...}), and fixing cities like"Bengaluru"→"Bangalore".
Common mistakes
- ❌ Counting matches played from
team1only. ✅ Addteam1andteam2counts. - ❌ Ranking by raw wins when teams played different numbers of matches. ✅ Compare win percentage.
- ❌ Averaging
marginacross runs and wickets. ✅ Group byresultfirst. - ❌ Using
.last()without sorting by date. ✅sort_values("date")first, so "last" really is the final. - ❌ Forgetting renamed franchises (Delhi Daredevils → Delhi Capitals) in the real data. ✅ Unify names with
replacebefore grouping.
Interview tip
Q: How do you test whether winning the toss helps win the match?
A: Create a boolean column toss_winner == winner and take its mean() — that is the share of matches the toss winner won. Then groupby("toss_decision") (or by season/venue) to see where it differs, and always look at the group count before trusting small-sample differences.
Key points to remember
- Convert date text with pd.to_datetime, then derive season with .dt.year — the first fix in any match dataset.
- Matches played = team1 counts + team2 counts; use .add(other, fill_value=0) to avoid NaN.
- Compare win percentage, not raw wins, when teams played different numbers of matches.
- The mean of a boolean column is the proportion of True — e.g. how often the toss winner won.
- pivot_table(index='winner', columns='season', aggfunc='count', fill_value=0) builds a wins-by-season table.
- Sort by date then groupby('season').last() gives each season's final.
- Group by result type before averaging margins — runs and wickets are different units.
Try it yourself
Load, convert dates, most wins and win %
Toss decisions and toss advantage
Wins by season, champions and venues
Practice: your own IPL question
Revision Flashcards
Tap a card to flip it and see the answer.
