Data Science & Machine Learning
Hands-onBeginner 40 min· Data foundations · 4 of 20

Exploratory data analysis and charts

Explore a year of coffee shop sales end to end: check data quality, spot outliers, make histograms, boxplots, scatter plots, a correlation heatmap and time charts, then write down what you found before modelling.

Exploratory data analysis (EDA) is what you do before any model: look at the data, check it for problems and find the patterns worth modelling. Good EDA prevents most bad models.

You will explore one year of daily sales for a coffee shop: how many cups were sold, the temperature, whether it was a weekend and whether a promotion ran. You will check quality, make six kinds of chart with matplotlib and seaborn, and write down what you found. Prerequisites: the previous lesson on pandas, and a Colab or Kaggle notebook.

Step 1: Create the practice data

The data below is made up by a program, so we know what pattern it hides, and every learner gets identical numbers (the random seed is fixed). The shop's average sales rise with temperature and on weekends and promotion days. Two problems are added on purpose: 8 missing temperatures and one wrong entry of 900 cups. Run this cell once. It saves sales.csv in your notebook.

Cell 1: generate sales.csv
import numpy as np
import pandas as pd

rng = np.random.default_rng(42)
n = 365
dates = pd.date_range("2025-11-01", periods=n, freq="D")
temp = 24 + 6 * np.sin((dates.day_of_year.to_numpy() - 80) / 365 * 2 * np.pi) + rng.normal(0, 2, n)
weekend = (dates.day_of_week >= 5).astype(int)
promo = rng.binomial(1, 0.2, n)
cups = 90 + 6 * (temp - 24) + 25 * weekend + 18 * promo + rng.normal(0, 10, n)

sales = pd.DataFrame({
    "date": dates,
    "temp_c": temp.round(1),
    "is_weekend": weekend,
    "promo": promo,
    "cups_sold": cups.round().astype(int),
})
# Make it messy, like real data: 8 missing temperatures and one wrong entry
sales.loc[rng.choice(n, 8, replace=False), "temp_c"] = np.nan
sales.loc[100, "cups_sold"] = 900
sales.to_csv("sales.csv", index=False)
print("saved sales.csv with", len(sales), "rows")

This is practice data, not real Brew & Bean data. You will use it again in the regression lesson.

Step 2: First look

Cell 2: load and inspect
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

sales = pd.read_csv("sales.csv", parse_dates=["date"])
print(sales.shape)
print(sales.head())
print(sales.isna().sum())
print(sales.describe().round(1))

Expected: 365 rows and 5 columns, temp_c has 8 missing values, and describe() shows cups_sold with a mean near 103 but a maximum of 900.

Read the describe() table slowly. The mean of 103 and median (50%) of 99 are close, but the max is 900 while 75% of days are 125 or less. One day is far away from the rest. That is an outlier. It could be a true busy day or a typing mistake; you must find out which.

Step 3: Check data quality

A quality checklist

  1. 1Missing values. temp_c has 8 missing. Decide how to handle them: here we fill with the median, because only 8 of 365 are missing.
  2. 2Outliers. Use the IQR rule: values below Q1 minus 1.5 times IQR, or above Q3 plus 1.5 times IQR, are flagged. IQR is Q3 minus Q1.
  3. 3Impossible values. A coffee shop selling 900 cups when the busiest normal day is about 185 is suspicious. In real work you would ask the owner or check the billing log. Here we remove it.
  4. 4Duplicates and types. Run sales.duplicated().sum() and check sales.dtypes. Both are fine in this dataset.
Cell 3: find and fix problems
q1, q3 = sales["cups_sold"].quantile([0.25, 0.75])
iqr = q3 - q1
low, high = q1 - 1.5 * iqr, q3 + 1.5 * iqr
print("normal range:", low, "to", high)
print(sales[(sales["cups_sold"] < low) | (sales["cups_sold"] > high)])

clean = sales[sales["cups_sold"] < 500].copy()
clean["temp_c"] = clean["temp_c"].fillna(clean["temp_c"].median())
print(len(clean), clean.isna().sum().sum())

The rule flags only the 900 row (the date shown is 2026-02-09). After cleaning you keep 364 rows and 0 missing values. We kept the original sales untouched and made clean.

Outliers are not always errors

Do not delete outliers just because they look odd. Remove a value only when you have a reason to believe it is an error. A real outlier (a festival day, a bulk order) may be exactly what your business cares about.

Step 4: One variable at a time

Cell 4: histogram and boxplot
fig, axes = plt.subplots(1, 2, figsize=(10, 4))

axes[0].hist(clean["cups_sold"], bins=20, color="#4C78A8", edgecolor="white")
axes[0].set_title("Cups sold per day")
axes[0].set_xlabel("Cups")
axes[0].set_ylabel("Number of days")

sns.boxplot(data=clean, x="is_weekend", y="cups_sold", ax=axes[1])
axes[1].set_title("Cups sold: weekday (0) vs weekend (1)")

plt.tight_layout()
plt.show()

The histogram shows the shape: where most days fall and how spread out they are. The boxplot shows the median (line), the middle 50% of days (box) and unusual days (dots). Weekend sales sit higher.

Use a histogram for the shape of one number. Use a boxplot to compare a number across groups. Use a bar chart for counts or averages of categories.

Step 5: Relationships between variables

Cell 5: scatter plot and group averages
sns.scatterplot(data=clean, x="temp_c", y="cups_sold", hue="promo", alpha=0.7)
plt.title("Hotter days sell more; promo days sell more at the same temperature")
plt.xlabel("Temperature (deg C)")
plt.ylabel("Cups sold")
plt.show()

print(clean.groupby("is_weekend")["cups_sold"].mean().round(1))
print(clean.groupby("promo")["cups_sold"].mean().round(1))

The points rise from bottom-left to top-right: a positive relationship with temperature. Colouring by promo shows promo days sit higher. Average cups: weekday about 93.6, weekend about 118.9; no promo about 96.8, promo about 116.7.

Cell 6: correlation heatmap
cols = ["temp_c", "is_weekend", "promo", "cups_sold"]
corr = clean[cols].corr().round(2)
print(corr)

sns.heatmap(corr, annot=True, cmap="coolwarm", vmin=-1, vmax=1)
plt.title("Correlation between columns")
plt.show()

The correlation of temp_c with cups_sold is about 0.85, a strong positive link. is_weekend is about 0.35 and promo about 0.25. Correlation runs from -1 to +1; 0 means no straight-line relationship.

A classic trap

Correlation is not causation. Ice-cream sales and drowning deaths both rise in summer, but ice cream does not drown anyone: heat drives both. Here we built the data, so we know temperature really drives sales. In real data, always ask what else could explain the link.

Step 6: Patterns over time

Cell 7: trend and weekday charts
monthly = clean.set_index("date")["cups_sold"].resample("MS").mean()
monthly.plot(kind="line", marker="o", figsize=(9, 3.5))
plt.title("Average cups per day, by month")
plt.ylabel("Cups")
plt.xlabel("Month")
plt.show()

clean["weekday"] = clean["date"].dt.day_name()
order = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
clean.groupby("weekday")["cups_sold"].mean().reindex(order).plot(kind="bar", figsize=(8, 3.5))
plt.title("Average cups by day of week")
plt.ylabel("Cups")
plt.show()

resample("MS") groups dates by month start. The line follows the seasons (temperature) and the bars show Saturday and Sunday above weekdays.

Step 7: Write down what you found

EDA ends with words, not just charts. A short written summary is what you hand to a manager or use to plan your model.

Cell 8: your findings
# Findings (written in a notebook text cell)
# 1. 365 rows; 8 missing temperatures filled with median; one 900-cup entry removed (billing error).
# 2. Sales swing with temperature: correlation about 0.85.
# 3. Weekends average about 25 cups more than weekdays; promo days about 20 more.
# 4. Next step: build a regression model using temp_c, is_weekend and promo.

Always record what you removed or changed and why, so someone can repeat or challenge your work.

Choosing the right chart

Chart chooser

QuestionChartCode
How is one number spread?Histogramplt.hist(x) or sns.histplot
Compare a number across groupsBoxplotsns.boxplot(x=, y=)
Do two numbers move together?Scatter plotsns.scatterplot(x=, y=)
Compare category totals or averagesBar chartplot(kind="bar")
How does a number change over time?Line chartplot(kind="line")
Which columns relate to each other?Heatmap of correlationssns.heatmap(corr)

Always give a chart a title, labelled axes and units. A chart that needs explanation in a meeting is a chart that is not finished.

Prompt · Review my EDA
I explored a dataset in pandas. Here is what I found:

[paste describe() output and your findings]

Act as a senior data scientist. Tell me: (1) what I may have missed in data quality, (2) which charts I should add, (3) any claims I made that could be wrong because of correlation vs causation, and (4) what I should check before building a model. Keep it to a short list.

What to expect: Good feedback names specific checks (e.g. duplicates, units, time order). Verify each suggestion on your data before accepting it.

Try it: Explore it yourself

Answer with code and a chart each: (1) Is the effect of a promotion bigger on weekends than on weekdays? (hint: group by two columns) (2) How many days had a temperature above 30 and what was the average cups sold on those days? (3) Remove the outlier check: what happens to the correlation of temperature and cups if you keep the 900 row?

Key takeaways

  1. EDA comes before modelling: check shape, types, missing values, duplicates and outliers first.
  2. Use describe(), histograms and boxplots for single columns; scatter plots, group averages and a correlation heatmap for relationships.
  3. Remove or fix an outlier only with a reason, and record what you changed.
  4. Correlation shows that two things move together, not that one causes the other.
  5. End EDA with a written summary that states the next step.

Check yourself

Finishing the quiz marks the lesson as done.

1. In describe(), the mean is 103, the median 99 and the max 900. What does this suggest?

2. You want to compare how sales are spread on weekdays versus weekends. Which chart fits best?

3. Two columns have a correlation of 0.85. What can you safely conclude?

4. Why create clean as a copy instead of editing sales directly?

5. The IQR rule flags a value. What should you do next?

Saved on this device. Create a free account to keep it on every device.

Practise this with Sam

A real mock interview, out loud, with an honest scorecard. Free to start.

Start free