Chapter 15 Exercises: Advanced Charts and Dashboards
Work through these exercises in order. Each tier builds on the previous. All exercises use Python 3.10+, seaborn, plotly express, and pandas. Where data is not provided, generate plausible business data using numpy with a fixed random seed.
Tier 1 — Foundational (Do These First)
Exercise 15-1: Your First seaborn Barplot
Create a DataFrame with the following sales data and draw a grouped bar chart using sns.barplot:
| Product | Q1 Sales | Q2 Sales | Q3 Sales |
|---|---|---|---|
| Laptops | 142000 | 158000 | 171000 |
| Monitors | 67000 | 73000 | 81000 |
| Keyboards | 23000 | 28000 | 31000 |
| Headsets | 41000 | 45000 | 52000 |
The chart should have:
- A descriptive title
- Dollar-formatted y-axis labels
- A legend labeling the quarters
- The "whitegrid" seaborn theme applied before drawing
Save the chart as product_sales_barplot.png.
Exercise 15-2: seaborn Theme Comparison
Draw the same chart from Exercise 15-1 four times in a 2×2 matplotlib grid, each with a different seaborn style: "whitegrid", "darkgrid", "white", and "ticks". Add a title to each subplot identifying the style name.
Use plt.subplots(2, 2, figsize=(14, 10)) and pass each ax to sns.barplot().
Save the result as theme_comparison.png. Write two sentences in a comment at the top of your file explaining which theme you would choose for a board-level presentation and why.
Exercise 15-3: Your First plotly Bar Chart
Reproduce the bar chart from Exercise 15-1 using plotly.express. Requirements:
- Use px.bar with barmode="group"
- Set the y-axis tick format to "$,.0f" using fig.update_layout
- Save the result as product_sales_interactive.html
- Add a hover tooltip that shows the quarter and revenue value clearly labeled
Open the HTML file in a browser and hover over several bars to verify the tooltips work.
Exercise 15-4: seaborn Line Plot
Using numpy with seed=42, generate 24 months of simulated revenue data (2022 and 2023) for a single company. The values should start around $800,000 per month in January 2022 and trend upward to around $1,100,000 by December 2023, with realistic monthly noise.
Draw a sns.lineplot with:
- Two lines: one for 2022, one for 2023
- marker="o" to show data points
- Month names on the x-axis (Jan through Dec)
- The "muted" seaborn color palette
Save as revenue_trend.png.
Exercise 15-5: plotly Line Chart with Hover Mode
Convert the line chart from Exercise 15-4 into a plotly interactive chart using px.line. Set hovermode="x unified" so hovering over any month shows both years' values simultaneously.
Add the following to the layout:
- The title "Revenue Trend: 2022 vs 2023"
- Y-axis formatted as "$,.0f"
- X-axis tick labels formatted as abbreviated month names
Save as revenue_trend_interactive.html.
Tier 2 — Developing Skills
Exercise 15-6: seaborn Heatmap
Generate a 6 × 12 matrix of data representing revenue by sales representative and month. Use the following rep names: Alice K., Bob L., Carol M., Dan N., Eve P., Frank Q. Use numpy with seed=55 to generate values between $45,000 and $180,000 per cell.
Draw a sns.heatmap with:
- The "Blues" colormap
- Cell annotations formatted as `"$XK"` (e.g., "$132K")
- A title "Monthly Revenue by Sales Rep — 2023"
- Rotated x-axis tick labels at 45 degrees to avoid crowding
- A color bar labeled "Revenue ($)"
Save as rep_revenue_heatmap.png.
Exercise 15-7: Diverging Heatmap for Performance vs Target
Using the same 6 × 12 rep/month structure from Exercise 15-6, create a second heatmap that shows percentage above or below target for each cell (assume each rep has a flat monthly target of $110,000).
Use the "RdYlGn" diverging colormap (red = below target, green = above target), with center=0 so that zero deviation is yellow. Annotate cells with the percentage (e.g., "+12%" or "-8%").
This exercise practices the important distinction between sequential and diverging color maps.
Exercise 15-8: seaborn Boxplot with Annotations
Generate order-value data for five sales reps: 80 orders each, with different median order values and spreads. Draw a sns.boxplot and:
- Add the median value as a text annotation above each box
- Overlay a
sns.stripplotwithjitter=Trueand 20% opacity - Use a custom color palette (one color per rep, not seaborn's default)
- Add a footnote text box explaining what the box and whiskers represent
Save as rep_order_boxplot.png.
Exercise 15-9: plotly Scatter with Trendlines
Using numpy with seed=88, generate 150 data points representing marketing campaigns with:
- spend: log-normally distributed, mean ~$8,000
- leads_generated: correlated with spend but with noise, roughly spend / 85
- channel: randomly assigned — Email, Social, Search, Events (4 categories)
Draw a px.scatter with:
- x = spend, y = leads_generated
- Color by channel
- trendline="ols" (requires statsmodels installed)
- Formatted axes (spend as dollars, leads as integers)
- Title: "Marketing Spend vs Leads Generated by Channel"
- Custom hover showing spend, leads, and channel
Save as marketing_scatter.html.
Exercise 15-10: plotly Sunburst Chart
Build a sunburst chart representing the following hierarchical sales data:
North America
├── USA
│ ├── Enterprise: $4.2M
│ ├── Mid-Market: $2.1M
│ └── SMB: $0.8M
└── Canada
├── Enterprise: $0.9M
└── Mid-Market: $0.5M
Europe
├── UK
│ ├── Enterprise: $1.8M
│ └── Mid-Market: $0.7M
└── Germany
├── Enterprise: $1.4M
└── Mid-Market: $0.6M
Use px.sunburst with path=[px.Constant("Global"), "continent", "country", "segment"]. Color by revenue value using "Blues" sequential scale. Save as global_revenue_sunburst.html.
Tier 3 — Applied Business
Exercise 15-11: The Complete seaborn Exploratory Dashboard
You are an analyst at a regional insurance company. Load or generate a dataset with 500 policies including columns: premium (annual premium), claims_count (0-5), policy_type ("Auto", "Home", "Life"), region ("North", "South", "East", "West"), and agent_years_experience (1-25 years).
Produce a 2×2 seaborn figure with:
- Top-left: Boxplot of premium by policy type
- Top-right: Heatmap of average premium by region × policy type (you'll need a pivot_table)
- Bottom-left: Scatter of agent experience vs average premium per client
- Bottom-right: Bar chart of claims count distribution (count of policies per claims value)
Save the entire figure as insurance_eda.png. Use consistent colors for policy type across all panels.
Exercise 15-12: plotly Dashboard — Two Metrics, One Panel
Sandra Chen has asked for a single chart showing both monthly revenue (bars) and cumulative revenue (line) for 2023. These are two different chart types on the same plot.
Use plotly.graph_objects (not plotly express) to build this:
- go.Bar for monthly revenue
- go.Scatter with mode="lines+markers" for cumulative sum
- Dual y-axes: left for monthly revenue, right for cumulative
- The cumulative line on yaxis="y2" with overlaying="y" and side="right"
- hovermode="x unified"
Save as monthly_cumulative_revenue.html.
Exercise 15-13: seaborn Pairplot for Business Insight
Generate a dataset of 100 accounts with four metrics: account_age_years, lifetime_revenue, churn_risk_score (0-100), and support_tickets_ytd. Include an industry category column with values: "Retail", "Healthcare", "Manufacturing", "Finance".
Draw a sns.pairplot with:
- hue="industry" for color
- diag_kind="kde" on the diagonal
- Semi-transparent points (alpha=0.6)
- A title above the entire grid
Inspect the result and write a 3-sentence interpretation: what correlation do you observe between any two variables, and what business action might that suggest?
Exercise 15-14: plotly Chart with Dropdown Filter
Build a plotly figure that shows monthly revenue as a bar chart, but includes a dropdown menu that lets the viewer switch between four regions. When a region is selected, only that region's data is visible.
Use plotly.graph_objects. Create one go.Bar trace per region. Use updatemenus with "buttons" to build the dropdown. Set the initial visible trace to "Northeast".
Save as regional_revenue_dropdown.html. This exercise teaches the mechanics of plotly dropdowns, which are useful whenever you want filter functionality without a full web framework.
Exercise 15-15: Saving Charts for Different Destinations
Take any plotly chart you've built in this chapter's exercises and save it in three formats:
1. chart_offline.html — fully self-contained HTML (no CDN)
2. chart_cdn.html — HTML using CDN for plotly.js (smaller file)
3. chart_print.png — static PNG at 1400px wide, scale=2 (requires kaleido)
Use os.path.getsize() to print the file sizes of all three. Write a comment explaining which format you would use in each of these scenarios:
- Emailing to a client with no Python access
- Embedding in a company intranet page
- Including in a printed quarterly report
Tier 4 — Challenge
Exercise 15-16: Animated plotly Bar Chart
Using plotly.express, create an animated bar chart showing the top 5 product categories by revenue for each quarter (Q1 through Q4). Use px.bar with animation_frame="quarter" and animation_group="category".
The chart should have: - Fixed x-axis range (so bars don't jump) - Play button that animates through quarters - Hover showing exact revenue per category per quarter
Save as animated_category_revenue.html.
Exercise 15-17: seaborn FacetGrid
Using a dataset of 8 sales reps, 4 regions, and 12 months (generate synthetically), draw a sns.FacetGrid that shows one line plot per region (4 rows) with all reps' monthly revenue on each subplot. Color lines by rep.
This requires: FacetGrid(data=..., row="region") followed by .map_dataframe(sns.lineplot, x="month", y="revenue", hue="rep_name").
Save as facet_rep_by_region.png. This exercise demonstrates how to produce small multiples — one of the most powerful techniques in data visualization.
Exercise 15-18: Full Business Dashboard
Build a complete self-contained plotly dashboard that a VP of Operations could use to monitor a distribution business. The dashboard should have at least five panels arranged in a grid:
- Monthly shipments: actual vs target (line chart)
- On-time delivery rate by warehouse (bar chart)
- Average days to ship by product category (horizontal bar chart)
- Inventory turnover by region (heatmap — use
go.Heatmap) - Delivery delay distribution (histogram — use
go.Histogram)
Use consistent colors. Write custom hover templates for every panel. Add a descriptive subtitle to the overall dashboard. Save as operations_dashboard.html.
Tier 5 — Mastery
Exercise 15-19: Reusable Visualization Functions
Write a Python module called biz_charts.py that provides the following reusable functions:
def revenue_heatmap(pivot_df: pd.DataFrame, title: str, output_path: str) -> None:
"""Draws a seaborn heatmap with standard business formatting."""
...
def trend_vs_target(
dates, actuals, targets, title: str, output_path: str
) -> go.Figure:
"""Returns a plotly figure with actual and target lines."""
...
def category_breakdown(
labels, values, title: str, output_path: str
) -> go.Figure:
"""Returns a plotly donut chart."""
...
Each function should:
- Apply consistent styling (theme, font, colors)
- Accept an output_path and save the chart automatically
- Include a Google-style docstring
- Handle edge cases (empty data, NaN values) gracefully
Demonstrate the module by importing it and generating three charts from a sample business dataset.
Exercise 15-20: The Interactive Client Report
Maya wants to give each of her clients a personalized interactive HTML report at year-end. The report for each client should contain:
- A line chart of that client's monthly revenue over the year
- A bar chart of hours spent per project for that client
- A summary table (using
go.Table) with: project name, billed amount, hours, effective rate, and status
Write a script that:
1. Reads client data from a CSV (generate it with at least 5 clients)
2. Loops over each client
3. Generates a separate HTML file per client (e.g., client_harrington_report.html)
4. Names each report with the client's name and the report date
This exercise brings together: data filtering, plotly subplots, go.Table, custom styling, write_html, and file naming conventions — the full production workflow.
Answer Guidance (Selected Exercises)
Exercise 15-1 (key points):
- Data must be reshaped to long format for sns.barplot with hue. Use pd.DataFrame with columns ["product", "quarter", "sales"] or use .melt().
- errorbar=None prevents seaborn from drawing confidence intervals on aggregated single-observation data.
Exercise 15-7 (key points):
- The deviation matrix: deviation = (revenue_matrix - 110000) / 110000
- sns.heatmap(..., center=0) ensures zero maps to the center of the diverging palette.
- Format annotations with f"{v:+.0%}" to show the + sign for positive values.
Exercise 15-12 (key points):
- fig.update_layout(yaxis2={"overlaying": "y", "side": "right"}) is the correct syntax for dual y-axes in graph objects.
- The cumulative line is df["revenue"].cumsum().
Exercise 15-14 (key points):
- Each go.Bar trace has visible=False except the first. Dropdown buttons set visible to a list of booleans matching trace count.
- Use "method": "update" (not "restyle") when updating both data and layout simultaneously.