Week 5: Data Visualization & EDA
Three things today:
Today is about orientation, not memorization. The companion notebook has every example we see — plus the ones we skip.
groupby?Anything still fuzzy from last week — now is the time.
What can you tell from this table?
| Week | Produce | Dairy | Frozen | Deli |
|---|---|---|---|---|
| 1 | 11,500 | 12,100 | 7,320 | 4,100 |
| 2 | 12,800 | 11,400 | 7,180 | 4,080 |
| 3 | 12,200 | 10,800 | 1,780 | 4,120 |
| 4 | 13,900 | 10,100 | 7,410 | 4,090 |
| 5 | 13,400 | 9,800 | 7,260 | 4,110 |
| 6 | 15,100 | 9,200 | 7,350 | 4,085 |
| 7 | 14,800 | 8,700 | 7,420 | 4,115 |
| 8 | 16,400 | 8,100 | 7,190 | 4,095 |
What can you tell from this plot?
Scenario 1
You just pulled the Complete Journey data and need to quickly check whether basket spend looks reasonable before writing any analysis code.
Scenario 2
You’re presenting weekly revenue trends to the VP of Marketing in a board meeting. The chart will appear in a formal slide deck.
Scenario 3
The promotions team wants a tool to interactively explore coupon redemption rates by store, product category, and week.
Same data. Three very different jobs. No single library is best at all three.
We should match how we visualize to the goal we have in mind.
| Purpose | Tool | Why It Wins Here |
|---|---|---|
| Quick EDA | Pandas .plot() |
One line, straight off a DataFrame |
| Statistical comparison | Seaborn | Handles grouping, ordering, and stats for you |
| Polished reporting | Matplotlib | Total control over every element |
| Interactive exploration | Bokeh | Renders to the browser — zoom, hover, filter |
Tip
These are not four competitors. Pandas and Seaborn are both built on top of Matplotlib — so they stack.
.plot() on any Series or DataFrame and pass kind=Tip
If you can answer your question in one line, Pandas .plot() is the right tool. Rule of thumb: if you’re typing a third line of formatting, you may want to switch libraries.
Swap kind= to change the chart: 'hist', 'box', 'line', 'bar', 'scatter', 'barh'.
| Pandas | Seaborn |
|---|---|
.plot(kind='hist') — basic bin counts |
histplot() — bins + KDE curve in one call |
.groupby().mean().plot.bar() — manual aggregation |
boxplot() / barplot() — handles stats automatically |
| No built-in group ordering | order= parameter on every chart |
Every Seaborn function follows one pattern: sns.function(data=df, x=…, y=…, hue=…, order=…)
fig, ax = plt.subplots(figsize=(10, 3.8))
sns.boxplot(
data=basket_demo,
x='income',
y='basket_spend',
order=income_order, # explicit ordering — critical for income brackets
ax=ax,
showfliers=False
)
ax.set_xticklabels(ax.get_xticklabels(), rotation=45, ha='right', fontsize=8)
ax.set_xlabel('Income bracket')
ax.set_ylabel('Basket spend ($)')
plt.tight_layout()
plt.show()Tip
Use Matplotlib when the chart needs to stand on its own — in a report, a slide deck, or a published figure. This is the library your project report will live on.
Everything in Matplotlib is an object you can reference and modify:
Once you have fig and ax, you control everything — including plots Seaborn or Pandas drew for you.
Fast, but it would not survive an executive meeting:
fig, ax = plt.subplots(figsize=(10, 3.5))
ax.plot(weekly_sales['week'], weekly_sales['total_sales'], linewidth=2)
# --- readable formatting ---
ax.set_title('Weekly total sales — note holiday spike in late December', pad=10)
ax.set_xlabel('Week')
ax.yaxis.set_major_formatter(mtick.StrMethodFormatter('${x:,.0f}'))
ax.grid(True, alpha=0.3)
for spine in ['top', 'right']:
ax.spines[spine].set_visible(False)
# --- annotate the insight ---
peak_idx = weekly_sales['total_sales'].idxmax()
peak_week = weekly_sales.loc[peak_idx, 'week']
peak_val = weekly_sales.loc[peak_idx, 'total_sales']
ax.annotate(
f'Holiday spike\n${peak_val:,.0f}',
xy=(peak_week, peak_val),
xytext=(peak_week - pd.Timedelta(weeks=8), peak_val * 0.97),
arrowprops=dict(arrowstyle='->', lw=1.2),
fontsize=9
)
plt.tight_layout()
plt.show()Caution
Matplotlib is powerful but verbose. There is a massive example gallery, and AI tools are very good at adapting Matplotlib code. The notebook walks this build step by step.
source = ColumnDataSource(weekly_sales)
p = figure(
title='Total Weekly Sales — hover, zoom, and pan to explore',
x_axis_type='datetime',
width=750, height=350,
tools='pan,wheel_zoom,box_zoom,reset,save'
)
p.line('week', 'total_sales', source=source,
line_width=2, color='steelblue')
hover = HoverTool(tooltips=[
('Week', '@week{%F}'),
('Sales', '@total_sales{$0,0}')
], formatters={'@week': 'datetime'})
p.add_tools(hover)
p.xaxis.axis_label = 'Week'
p.yaxis.axis_label = 'Total Sales ($)'
show(p)Tip
Run this in the companion notebook to see the interactive version — it won’t fully render in a static slide.
Exercise 1 — Run & Compare 💻
Open the Exercise 1 section of the companion notebook. Every cell is ready to run — you don’t write any code today.
Run them top to bottom. Same Complete Journey data, drawn four ways:
Answer these two as you go:
5 minutes — run, look, compare.
Today was a tour. The readings are where you learn to drive.
| Chapter | What it teaches you | Where it shows up in the project |
|---|---|---|
| 13 — Pandas | .plot() for fast exploratory charts |
How you’ll explore your own data |
| 14 — Seaborn / Matplotlib / Bokeh | statistical charts, full control, interactivity | How you’ll build report-quality figures |
| 15 — EDA | a complete case study, start to finish | The model for your entire report |
20 of the project’s 75 points ride on your charts
EDA (10 pts) and Findings (10 pts) are graded on whether your visuals reveal something non-obvious and are clearly narrated. Code & Craft and Delivery lean on them too.
Nobody produces professional-looking figures on the first attempt. The reps come from working through these chapters and applying them to your own data.
| You learned | Which lets you |
|---|---|
| Subsetting & filtering (Wk 3) | isolate the rows a question is actually about |
groupby & aggregation (Wk 4) |
compute statistics at any level — household, basket, department, week |
| Joins (Wk 4) | pull in the context that makes a comparison possible |
| Visualization (this week) | see the shape, catch what’s wrong, show the finding |
Aggregation tells you what the numbers are. Visualization tells you what they mean and helps you communicate them. Together, that is most of an EDA.
Important
What’s left is understanding how to ask a good question of our data.
That’s what the rest of Part 2 is about.
Exploratory Data Analysis is not a checklist of charts to run.
It is a sequence of questions — each answer raising the next question.
Data dump (avoid)
Real EDA (aim for)
Important
The goal is not to show that you looked at the data. The goal is to find something worth saying.
flowchart LR
A["1️⃣ Question"] --> B["2️⃣ Structure"]
B --> C["3️⃣ Distributions"]
C --> D["4️⃣ Segmentation"]
D --> E["5️⃣ Story"]
style A fill:#2c3e50,color:#fff
style B fill:#2980b9,color:#fff
style C fill:#27ae60,color:#fff
style D fill:#e67e22,color:#fff
style E fill:#8e44ad,color:#fff
| Step | Question to ask |
|---|---|
| Question | What do I actually want to know? Is it specific enough to know when I’ve answered it? |
| Structure | What shape is the data? What do I need to join? Where are the missing values? |
| Distributions | What does the outcome variable look like across all households? Any outliers? |
| Segmentation | Does the pattern differ across groups? Where does the interesting variation live? |
| Story | What changed? What surprised me? What should the decision-maker do next? |
The question determines everything that follows.
Caution
Too broad: “How do customers shop?”
This can’t be answered — no unit of analysis, no metric, no comparison to make.
Tip
Sharp: “Do households that redeem coupons have larger basket spend than those that don’t — and does this differ across income brackets?”
This names: the unit (basket), the metric (spend), the comparison (coupon vs. no coupon), and the segmentation (income).
A sharp EDA question tells you which tables to join, which columns to compute, and what a result would look like.
Note
We are going to answer this exact question over the next four slides — one step of the framework at a time.
Our question compares baskets, but transactions gives us 1.5 million item rows. So the first move is structural: collapse to one row per basket, flag which ones used a coupon, then attach household demographics for the income half of the question.
Every one of those steps can quietly lose rows — so count what survives:
Baskets: 155,848
... that used a coupon: 9,827 (6.3%)
Households: 2,469
After joining demographics: 81,817 baskets (52% kept)
... households: 801
Warning
The join cost us half the data. Only 801 households have demographics. Any income comparison we make describes those households — not all shoppers. Notice this now, not in your conclusion.
Before comparing any groups, look at the outcome variable alone: what does a typical basket even look like?
The mean sits $12 above the median — a handful of enormous baskets drag it to the right.
Report the median — and say why you did
With skew this strong, the mean describes a basket almost nobody actually has. The median is what a typical trip looks like, and it barely moves when a few $300 stock-ups show up.
Put that reasoning in your own report — one sentence is enough: “We compare medians because basket spend is right-skewed.” It tells your reader the choice was deliberate rather than a default, and defending a judgment call like this is exactly what the EDA portion of the rubric rewards.
And across income brackets? The lift shows up in every bracket — 100% to 290%, with no clean upward or downward pattern.
Note
Let’s look, rather than assert.
Orange clears blue in every single bracket. Both series drift up with income — so the real question is whether the gap widens.
Note
It doesn’t. As a ratio the lift bounces from roughly 100% up to 290% with no clean direction. Richer households spend more per trip, coupon or not — but the coupon effect itself is not an income story. The second half of our question resolves to “no,” and ruling something out is progress, not failure.
Warning
And notice the shaded brackets: those medians rest on 5–11 households. That is the Step 2 join loss surfacing exactly where you’d be most tempted to read a trend.
A 3.3x gap is tempting. Push one question further before you recommend anything.
Spend per item is identical. Coupon baskets are not pricier — they are simply bigger trips.
The “So What” Test
So what? Coupon redemption marks the weekly stock-up trip, not a change in what people buy.
Caution
The recommendation we cannot make: “Send more coupons to grow basket size.”
Nothing here shows coupons caused bigger baskets. Shoppers planning a big trip may be the ones who bother to bring a coupon.
Next question: do the same households shop differently with and without a coupon? That comparison could support a causal claim. This one can’t.
Exercise 2 — What’s the Next Question? 🤔
We concluded: coupon baskets are 3.3x larger, but spend per item is identical — coupons mark stock-up trips.
Your VP of Promotions reads this and asks: “So should we mail more coupons?”
Name 3 follow-up analyses you would run before answering, and what each would tell you.
Discuss with your neighbor for 3 minutes.
You are a data scientist at Regork, a national grocery chain.
Your manager has asked you to identify a potential growth opportunity — an area where the company could invest future resources to increase revenue or profits.
Your task:
Tip
Groups: 2–4 students. Join one of the pre-defined groups in Canvas → People → Mid-Term Project. You may not create your own group outside that list.
You just watched one get answered
“Do households that redeem coupons have larger basket spend than those that don’t — and does this differ across income brackets?”
That question survived contact with the data because it named four things up front:
| It named | Which told us |
|---|---|
| A unit — the basket | what one row of our table had to be |
| A metric — spend | what to compute, and that skew mattered |
| A comparison — coupon vs. not | what the chart at Step 4 had to show |
| A segmentation — income | what to check next, even though it came back empty |
Notice it was answerable in four steps — and we knew we were done. That is the bar.
Private label
“Do households with kids buy a higher share of private-label products than households without — and does that gap differ by department?”
Unit: household · Metric: private-label share of spend · Compare: kids vs. none · Segment: department
Trip behavior
“Do households that shop twice a week or more spend more over the year than occasional shoppers — or do they spread the same annual spend across more trips?”
Unit: household · Metric: annual spend & trip count · Compare: frequent vs. occasional · Segment: income
Promotions
“Do products featured in the weekly mailer sell more during the featured week — and does any lift carry into the week after?”
Unit: product-week · Metric: units sold · Compare: featured vs. not · Segment: department
Note
Notice the second one has the same shape as our coupon finding: “more spend” and “more trips” are different answers, and the question is built so you can tell them apart. Design that distinction in from the start — don’t discover it at the end.
A question worth asking — specific, answerable, and tied to a decision someone actually makes. You saw the standard in Part 2.
A named decision-maker — who acts on this, and what do they do differently on Monday? The VP of Promotions needs a recommendation, not a summary of what you computed.
Reasoning you can follow — structure, then distributions, then segmentation. Each step raises the next question. Show the reasoning, not just the outputs.
Honesty about limits — say what your analysis does not establish. The strongest projects name the recommendation they can’t yet make, and what would be needed to make it.
Tip
Every chart earns its place by making one point. Label the axes, use a readable scale, and narrate what the reader should see. Plots without interpretation are decoration.
Don’t guess at the bar — go look at it
The Canvas project page includes real report and presentation submissions from previous classes. They are the clearest picture you will get of what “strong” actually means.
Before you write anything, read two of them end to end and ask: what did they investigate, how did they narrate each chart, and where did they choose to stop? An hour spent there will do more for your grade than an extra hour of code.
YYYY_BANA4080_groupXX_finalproject.htmlNote
One group member submits both deliverables together. Submit early — large video files can take time to upload.
75 points total: written report (40) + recorded presentation (35).
Report — 40 pts
| Section | Pts |
|---|---|
| Introduction | 5 |
| Libraries | 5 |
| EDA | 10 |
| Summary | 5 |
| Code & Craft | 15 |
Presentation — 35 pts
| Section | Pts |
|---|---|
| Introduction | 5 |
| Findings | 10 |
| Summary | 5 |
| Delivery | 15 |
Canvas → Assignments → Mid-Term Project
Full description, the complete rubric, and example submissions from a previous course. Reading the rubric before you start is one of the highest-leverage things you can do.
Tip
EDA (10 pts) is where projects separate. One non-obvious pattern explained well beats five polished charts that say nothing new.
In Thursday’s lab you will:
Tips for a Strong Pitch
After lab: refine your idea into a formal proposal (the homework due this week).
Open floor for questions on:
BANA 4080 | Week 5