Redirect Chapter 17: EDA Case Studies | AI Fundamentals
← Back to Tutorials Chapter 17

EDA Case Studies

EDA as a Habit

Exploratory Data Analysis (EDA) is the process of getting to know a dataset before modelling it: checking shapes and types, hunting for missing values and outliers, and asking the dataset questions through summary statistics and plots. In this chapter we run three realistic case studies end-to-end so the cleaning and visualization skills from earlier chapters become muscle memory. Treat each one as a recipe you can apply to any new dataset you meet.

Case Study 1: Red Wine Dataset EDA

The classic Red Wine Quality dataset contains about 1,600 bottles of red wine, each described by 11 chemical features (fixed acidity, volatile acidity, citric acid, residual sugar, chlorides, free and total sulfur dioxide, density, pH, sulphates, alcohol) plus a quality score from 0 to 10.

Univariate Analysis

Start one column at a time. Check each feature's data type, count of missing values, mean, median, and spread, then plot histograms. Two findings stand out immediately in this dataset: density is nearly constant (its histogram is a razor-thin spike) while residual sugar is heavily right-skewed. No values are missing — the dataset is clean — but the quality score itself is skewed toward 5 and 6, with very few wines scoring below 3 or above 8.

import pandas as pd

df = pd.read_csv("winequality-red.csv")
print(df.info())
print(df.describe().T)
df.hist(figsize=(12, 10), bins=30)

Multivariate Analysis

Next, relationships between features. A correlation heatmap reveals that alcohol correlates positively with quality, while volatile acidity and density correlate negatively with it. Sulfur dioxide features correlate strongly with each other (a redundancy to remember for modelling). Pairplots or scatter plots confirm whether these correlations are linear and whether any outliers are driving them.

Quality Analysis

Finally, relate everything to the target. Group the data by quality score and compare the average of each feature; a table of group means quickly shows which chemistry separates good wine from bad. A common modelling trick for this dataset is to convert quality into a binary target (“good” if score ≥ 7, “not good” otherwise), which gives a clean classification problem instead of an ordinal regression one.

Takeaway: always close the loop from “what does one column look like” to “how do columns relate” to “what drives the target.” That three-step rhythm — univariate, multivariate, target-focused — is the skeleton of every EDA.

Case Study 2: Flight Price Dataset — EDA and Feature Engineering

Flight-price datasets (with columns like airline, source and destination cities, departure and arrival times, total stops, and price) are dirtier than the wine data: strings, timestamps, and clear interaction effects.

Initial Sweep

Load the data and inspect. The price column is numeric; check its distribution for a long right tail and note that a log transform may help later. Airline, source, and destination are categorical — decide which encoding to use (one-hot for low-cardinality columns).

Feature Engineering

The raw columns hide most of the signal, so we engineer new ones:

def parse_time(t):
    h, m = t.split(":")
    return int(h) + int(m) / 60

df["dep_hours"] = df["dep_time"].apply(parse_time)
df["arr_hours"] = df["arr_time"].apply(parse_time)
df["duration"] = (df["arr_hours"] - df["dep_hours"]) % 24

Validation Checks

Cross-tabulate price against airline and stops, and box-plot price by day of week. These two visual checks immediately confirm which engineered features carry signal. Check for duplicate rows (identical flights listed twice are common in scraped data) and drop them before modelling.

Takeaway: domain knowledge is feature engineering. Nothing in a flight dataset changes price more than route, timing, and demand cycles — and those exist only if you build them from raw strings and dates.

Case Study 3: Data Cleaning with the Google Play Store Dataset

The Google Play Store Apps dataset is a favourite for practising real-world cleaning because it is messier than the wine data and gets messier in the ways real data does: mixed types, units inside numbers, and a corrupted-looking price column.

Load and Inspect

The dataset holds app name, category, rating, reviews count, size, installs, price, content rating, and genres. The first sweep reveals the traps:

def clean_installs(s):
    s = str(s).replace("+", "").replace(",", "")
    if s == "0" or s == "Free":
        return 0
    return int(s)

df["installs_clean"] = df["installs"].apply(clean_installs)

Cleaning Steps

  1. Convert installs to a clean numeric column with the function above; keep the categorical “bucket” version too if you want both views.
  2. Strip the “$” from price and convert to float; drop or fix the rogue “Everyone” rows.
  3. Parse size into megabytes: extract the number, detect the unit, and divide kilobytes by 1024; assign NaN to “Varies with device.”
  4. Drop duplicate app names, keeping the first occurrence.
  5. Verify every column now has the right dtype with df.dtypes and re-check missing-value counts.

Case Study 3 Part 2: EDA on the Cleaned Play Store Data

With clean columns, the analysis can finally happen. Now we answer real questions with plots and tables.

Ratings and Reviews

Most apps sit between 4.0 and 4.5 stars, and the rating distribution has a strong left tail of poorly rated apps. Log-scale the reviews count — a few blockbuster apps dominate, and the skew is dramatic.

Category Breakdown

Count apps per category with a bar chart: FAMILY, GAME, and TOOLS typically dominate, while a long tail of categories each hold a handful of apps. Then explore whether price differs by category — game apps skew free with a few premium outliers, while medical and business categories have more paid apps.

Size, Installs, and Price

Scatter plots of size vs rating and installs vs rating usually show no strong linear trend — quality is only weakly tied to size or popularity. The big insight for any later model is that installs and reviews are heavily skewed, so a log transform will matter more than any single engineered column.

Content Rating and Genres

Cross-tab content rating against average rating, and compare the average installs for “Everyone” versus “Mature 17+” apps. Genres overlap categories, so inspect unique genre strings before deciding whether to keep both columns.

import seaborn as sns

sns.histplot(df_clean["rating"], bins=30)
sns.boxplot(data=df_clean, x="category", y="rating")
sns.scatterplot(data=df_clean, x="size_mb", y="installs_clean", alpha=0.3)
Exercise: Load any public CSV you can find (or reuse the Play Store one) and reproduce the full EDA loop: (1) inspect dtypes and missing values; (2) clean the two messiest columns; (3) engineer at least three new features that encode time, text, or combinations of existing columns; (4) produce one histogram, one box plot, one correlation heatmap, and one pair of grouped means against the target; and (5) write a short paragraph naming the three most important drivers of your target and the strongest warning signs you saw in the data.