Free GuideIntermediate
50 min read
#Data Visualization#Matplotlib#Seaborn#Histograms#Boxplots#Heatmaps

08. Data Visualization for Exploratory Analysis

Visualizing analytical patterns: Matplotlib figure/axes architectures, Seaborn statistical plots, distribution histograms/KDEs, box/violin comparisons, scatter matrices, and heatmaps.

Notebook 8: Data Visualization for EDA

Module: Data Analytics Level: Intermediate Prerequisites: Notebook 7 (Exploratory Data Analysis Techniques) Estimated Time: 4 hours


Table of Contents#

  1. Why Visualization Is Not Optional
  2. Matplotlib Fundamentals
  3. Seaborn: A Statistical Layer on Matplotlib
  4. Visualizing a Single Numeric Variable
  5. Visualizing a Single Categorical Variable
  6. Visualizing Two Numeric Variables
  7. Visualizing Numeric vs. Categorical
  8. Visualizing Two Categorical Variables
  9. Visualizing Three or More Variables
  10. The Chart Selection Framework
  11. Common Chart Design Mistakes
  12. Hands-On: A Complete Visual EDA Pass
  13. Case Studies
  14. Common Misconceptions and Pitfalls
  15. Glossary
  16. Exercises
  17. Quiz
  18. Summary
  19. Further Reading

Learning Objectives#

By the end of this notebook, you will be able to:

  • Explain why visualization reveals patterns that numeric summaries alone can miss
  • Create basic plots using Matplotlib and understand its figure/axes architecture
  • Use Seaborn to create statistically-aware plots with less code than raw Matplotlib
  • Select the appropriate plot type for a single numeric variable, a single categorical variable, and combinations of both
  • Build and interpret histograms, box plots, violin plots, scatter plots, bar charts, and heatmaps
  • Apply a systematic chart selection framework based on variable types and count
  • Identify and avoid common chart design mistakes that mislead rather than inform

1. Why Visualization Is Not Optional#

Anscombe's Quartet, a famous 1973 construction by statistician Francis Anscombe, consists of four datasets that share nearly identical summary statistics (mean, variance, correlation) but look dramatically different when plotted: one is a clean linear relationship, one is a clear curve, one has a single outlier distorting an otherwise perfect line, and one is a vertical line of points with a single outlier creating an artificial correlation. This single example is the strongest possible argument for why Notebook 6 and Notebook 7's numeric techniques must be paired with visualization: a table of summary statistics can be identical for datasets with completely different underlying structure, and only a plot reliably reveals which situation you are actually looking at.

Python
import matplotlib.pyplot as plt import numpy as np # A simplified illustration of the Anscombe's Quartet principle x = np.array([10, 8, 13, 9, 11, 14, 6, 4, 12, 7, 5]) y1 = np.array([8.04, 6.95, 7.58, 8.81, 8.33, 9.96, 7.24, 4.26, 10.84, 4.82, 5.68]) y2 = np.array([9.14, 8.14, 8.74, 8.77, 9.26, 8.10, 6.13, 3.10, 9.13, 7.26, 4.74]) print(f"Correlation (linear-looking): {np.corrcoef(x, y1)[0,1]:.3f}") print(f"Correlation (curved-looking): {np.corrcoef(x, y2)[0,1]:.3f}")

Expected output:

›Correlation (linear-looking): 0.816 Correlation (curved-looking): 0.816

Both pairs have nearly identical correlation, yet plotting them (Section 6) reveals one is a genuine linear relationship and the other is a clear curve poorly described by a single correlation number, directly foreshadowing the correlation-versus-visualization caution developed further in Notebook 9.


2. Matplotlib Fundamentals#

2.1 The Figure and Axes Architecture#

Matplotlib organizes every plot around two core objects: the Figure (the overall canvas/window) and one or more Axes (individual plot areas within that figure). Understanding this distinction is essential for building anything beyond a single simple plot.

Python
import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(8, 5)) ax.plot([1, 2, 3, 4], [10, 15, 13, 18]) ax.set_title("Basic Line Plot") ax.set_xlabel("Time Period") ax.set_ylabel("Value") plt.show()

2.2 Multiple Subplots#

Python
fig, axes = plt.subplots(1, 2, figsize=(12, 5)) axes[0].plot([1, 2, 3], [4, 5, 6]) axes[0].set_title("Left Plot") axes[1].bar(["A", "B", "C"], [10, 15, 7]) axes[1].set_title("Right Plot") plt.tight_layout() plt.show()

plt.tight_layout() automatically adjusts spacing between subplots to prevent overlapping labels and titles, a small but frequently necessary finishing step.

2.3 The Object-Oriented vs. Pyplot Interface#

Matplotlib offers two overlapping styles: the object-oriented interface used above (explicitly creating and calling methods on fig and ax objects) and a simpler, implicit plt.plot(...), plt.title(...) style that operates on a hidden "current" figure. The object-oriented style, while slightly more verbose, is generally recommended for anything beyond a single trivial plot, since it removes ambiguity about which figure or axes a given command applies to, particularly once multiple subplots are involved (Section 2.2).


3. Seaborn: A Statistical Layer on Matplotlib#

3.1 Why Seaborn Exists#

Seaborn is built on top of Matplotlib and is specifically designed for statistical visualization, providing higher-level functions that handle common EDA plotting needs (distributions, categorical comparisons, relationships) with less code, better default styling, and built-in statistical computations (like confidence intervals) that would require substantial manual work in raw Matplotlib.

Python
import seaborn as sns import matplotlib.pyplot as plt sns.set_theme(style="whitegrid") # a commonly used clean default style fig, ax = plt.subplots(figsize=(8, 5)) sns.histplot(data=df, x="monthly_spend", bins=30, ax=ax) ax.set_title("Distribution of Monthly Spend") plt.show()

3.2 Working With DataFrames Directly#

A key Seaborn design feature: most functions accept a data= DataFrame argument along with column names as strings for x, y, hue, and similar parameters, rather than requiring you to extract raw arrays manually, as Matplotlib typically requires. This tight integration with Pandas DataFrames is precisely why Seaborn is generally the more efficient tool for the kind of DataFrame-centric EDA developed throughout this course, with Matplotlib remaining available underneath for fine-grained customization when needed.


4. Visualizing a Single Numeric Variable#

4.1 The Histogram#

A histogram divides a numeric variable's range into bins and shows the count (or density) of observations falling into each bin, the most direct visual representation of a distribution's shape, directly connecting to the skewness and kurtosis measures from Notebook 6, Sections 4.1 and 4.2.

Python
import seaborn as sns import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(8, 5)) sns.histplot(data=df, x="monthly_spend", bins=30, kde=True, ax=ax) ax.set_title("Distribution of Monthly Spend") plt.show()

The kde=True parameter overlays a Kernel Density Estimate, a smoothed curve approximating the underlying continuous distribution, which can make the overall shape (symmetric, skewed, bimodal) easier to perceive than the raw, sometimes jagged bin heights alone.

4.2 Choosing the Number of Bins#

The number of bins substantially affects a histogram's interpretability: too few bins can hide real structure (like multiple modes), while too many can make the plot look noisy and obscure the overall shape rather than revealing it. There is no single universally correct number, but starting with a moderate number (20-50 for most datasets) and adjusting based on what reveals structure clearly is standard practice.

Python
fig, axes = plt.subplots(1, 3, figsize=(15, 4)) for ax, bins in zip(axes, [5, 30, 200]): sns.histplot(data=df, x="monthly_spend", bins=bins, ax=ax) ax.set_title(f"bins={bins}") plt.tight_layout() plt.show()

4.3 The Box Plot#

A box plot (or box-and-whisker plot) visually summarizes the five-number summary from Notebook 6, Section 5.2: the box spans the interquartile range (Q1 to Q3), the line inside the box marks the median, and the whiskers typically extend to 1.5 times the IQR beyond the box, with points beyond that flagged individually as potential outliers, directly visualizing the IQR outlier detection rule from Notebook 4, Section 4.2.

Python
fig, ax = plt.subplots(figsize=(6, 5)) sns.boxplot(data=df, y="monthly_spend", ax=ax) ax.set_title("Box Plot of Monthly Spend") plt.show()

4.4 The Violin Plot#

A violin plot combines a box plot's summary information with a KDE-style density shape on either side, showing the full distribution shape (including multiple modes, which a plain box plot cannot reveal) alongside the standard summary statistics:

Python
fig, ax = plt.subplots(figsize=(6, 5)) sns.violinplot(data=df, y="monthly_spend", ax=ax) ax.set_title("Violin Plot of Monthly Spend") plt.show()

4.5 Choosing Between Histogram, Box Plot, and Violin Plot#

Plot TypeShows Full ShapeShows Outliers ExplicitlyBest For
HistogramYesOnly visually, not flaggedA single distribution's detailed shape
Box plotNo (only 5-number summary)Yes, explicitly plotted as pointsComparing several groups compactly (Section 7)
Violin plotYesLess explicitly than a box plotComparing full distribution shape across several groups

5. Visualizing a Single Categorical Variable#

5.1 The Bar Chart#

The standard tool for a single categorical variable's frequency counts, directly visualizing the .value_counts() output from Notebook 7, Section 3.2:

Python
fig, ax = plt.subplots(figsize=(8, 5)) sns.countplot(data=df, x="subscription_tier", order=df["subscription_tier"].value_counts().index, ax=ax) ax.set_title("Customer Count by Subscription Tier") plt.show()

The order parameter, sorting bars by frequency rather than the default (often alphabetical or insertion) order, is a small but valuable improvement that makes the most and least common categories immediately visually apparent.

5.2 Bar Charts vs. Pie Charts#

Pie charts are frequently used for categorical proportions but are widely considered, based on substantial visualization research, to be harder for humans to accurately compare than a simple bar chart, since human perception is considerably better at comparing the lengths of aligned bars than the angles or areas of pie slices, especially once more than two or three categories are involved. A bar chart is generally the better default choice for this reason, with a pie chart reserved for cases where the "whole equals 100 percent" framing is specifically the point being emphasized and only a small number of categories are involved.


6. Visualizing Two Numeric Variables#

6.1 The Scatter Plot#

The primary tool for examining the relationship between two numeric variables, directly setting up the correlation analysis in Notebook 9:

Python
fig, ax = plt.subplots(figsize=(8, 6)) sns.scatterplot(data=df, x="account_age_months", y="monthly_spend", ax=ax) ax.set_title("Monthly Spend vs. Account Age") plt.show()

6.2 Adding a Trend Line#

Python
fig, ax = plt.subplots(figsize=(8, 6)) sns.regplot(data=df, x="account_age_months", y="monthly_spend", scatter_kws={"alpha": 0.4}, ax=ax) ax.set_title("Monthly Spend vs. Account Age, With Trend Line") plt.show()

sns.regplot overlays a fitted linear regression line (previewing the concepts from the Machine Learning course's Notebook 3) along with a shaded confidence band, providing a quick visual sense of the direction and apparent strength of a linear relationship, directly complementing the numeric correlation coefficient covered in Notebook 9.

6.3 Handling Overplotting#

With a very large number of points, a scatter plot can become an indistinguishable, solid mass of overlapping markers, a problem called overplotting. Two common remedies:

Python
# Remedy 1: transparency (alpha), so overlapping points appear darker sns.scatterplot(data=df, x="account_age_months", y="monthly_spend", alpha=0.2) # Remedy 2: a hexbin or 2D density plot, aggregating points into # spatial bins, showing density via color rather than individual markers fig, ax = plt.subplots(figsize=(8, 6)) sns.histplot(data=df, x="account_age_months", y="monthly_spend", bins=30, cbar=True, ax=ax) plt.show()

6.4 The Pair Plot#

For a first-pass overview of pairwise relationships among several numeric variables simultaneously, directly connecting to the multivariate correlation matrix preview from Notebook 7, Section 5.3:

Python
sns.pairplot(df[["monthly_spend", "account_age_months", "num_support_tickets"]]) plt.show()

This produces a grid of scatter plots for every pair of variables, with histograms along the diagonal, providing a compact multivariate visual summary in a single call, though it can become visually cluttered and slow to render with more than roughly 5-6 variables at once.


7. Visualizing Numeric vs. Categorical#

7.1 Grouped Box Plots#

Directly visualizing the grouped summary statistics from Notebook 7, Section 4.2:

Python
fig, ax = plt.subplots(figsize=(9, 6)) sns.boxplot(data=df, x="subscription_tier", y="monthly_spend", ax=ax) ax.set_title("Monthly Spend by Subscription Tier") plt.show()

This single plot conveys, for every tier simultaneously, the median, IQR, and outlier structure, a dramatically more information-dense and immediately comparable presentation than a table of numbers alone.

7.2 Grouped Violin Plots#

Python
fig, ax = plt.subplots(figsize=(9, 6)) sns.violinplot(data=df, x="subscription_tier", y="monthly_spend", ax=ax) ax.set_title("Monthly Spend Distribution by Subscription Tier") plt.show()

A grouped violin plot is particularly valuable when a box plot's summary alone might hide an important shape difference, such as one tier having a bimodal distribution (two distinct customer sub-populations) that a box plot's five-number summary cannot reveal but a violin plot's density shape immediately shows.

7.3 Bar Charts of Group Means#

Python
fig, ax = plt.subplots(figsize=(9, 6)) sns.barplot(data=df, x="subscription_tier", y="monthly_spend", errorbar="sd", ax=ax) ax.set_title("Average Monthly Spend by Tier, with Standard Deviation") plt.show()

sns.barplot shows the mean of each group by default (not a raw count, which is what countplot, Section 5.1, is for), with error bars representing variability (here, standard deviation, though confidence intervals are another common choice); it is important not to confuse barplot (means with error bars) with countplot (frequency counts), a common naming-based confusion.


8. Visualizing Two Categorical Variables#

8.1 The Heatmap of a Cross-Tabulation#

Directly visualizing the cross-tabulation from Notebook 5, Section 8:

Python
crosstab = pd.crosstab(df["region"], df["subscription_tier"]) fig, ax = plt.subplots(figsize=(8, 6)) sns.heatmap(crosstab, annot=True, fmt="d", cmap="Blues", ax=ax) ax.set_title("Customer Count: Region vs. Subscription Tier") plt.show()

annot=True overlays the actual numeric count on each cell, combining the precision of a table with the pattern-recognition strength of a color-based heatmap, generally the single best tool for visualizing the relationship between two categorical variables.

8.2 Grouped/Stacked Bar Charts#

Python
crosstab_pct = pd.crosstab(df["region"], df["subscription_tier"], normalize="index") crosstab_pct.plot(kind="bar", stacked=True, figsize=(9, 6)) plt.title("Subscription Tier Distribution by Region") plt.ylabel("Proportion") plt.legend(title="Tier", bbox_to_anchor=(1.05, 1)) plt.tight_layout() plt.show()

A stacked bar chart of normalized proportions (Notebook 5, Section 8.2) is particularly effective for comparing the composition of each category across groups, complementing the heatmap's strength at showing raw magnitude.


9. Visualizing Three or More Variables#

9.1 Encoding a Third Variable With Color (Hue)#

Seaborn's hue parameter maps a third variable (typically categorical) to color, extending any of the two-variable plots above into a basic multivariate visualization:

Python
fig, ax = plt.subplots(figsize=(9, 6)) sns.scatterplot(data=df, x="account_age_months", y="monthly_spend", hue="subscription_tier", ax=ax) ax.set_title("Monthly Spend vs. Account Age, Colored by Tier") plt.show()

9.2 Encoding a Fourth Variable With Size or Style#

Python
fig, ax = plt.subplots(figsize=(9, 6)) sns.scatterplot( data=df, x="account_age_months", y="monthly_spend", hue="subscription_tier", size="num_support_tickets", ax=ax ) plt.show()

Stacking too many encodings (color, size, shape, faceting) onto a single plot quickly becomes difficult to read; generally no more than two or three variables should be encoded visually within a single plot before splitting into multiple panels (Section 9.3) becomes the clearer choice.

9.3 Faceting: Small Multiples#

Rather than cramming more variables into a single plot via color and size, faceting creates a grid of small, identical subplots, each showing a different subset of the data (typically defined by a categorical variable), a technique often more readable than heavy color/size encoding once more than two or three dimensions are involved:

Python
g = sns.FacetGrid(df, col="region", col_wrap=2, height=4) g.map_dataframe(sns.scatterplot, x="account_age_months", y="monthly_spend") g.set_titles("{col_name}") plt.tight_layout() plt.show()

This produces one scatter plot per region, arranged in a grid, allowing direct visual comparison of the age-versus-spend relationship (Section 6) across regions simultaneously, directly serving the multivariate and drill-down goals from Notebook 7, Sections 5 and 7, in visual form.


10. The Chart Selection Framework#

SituationRecommended Chart
One numeric variable, examine full shapeHistogram (Section 4.1), optionally with KDE
One numeric variable, compact summaryBox plot (Section 4.3)
One categorical variableBar chart of counts (Section 5.1)
Two numeric variablesScatter plot (Section 6.1), optionally with trend line
Several numeric variables, pairwise overviewPair plot (Section 6.4)
Numeric vs. categorical, compare distributions across groupsGrouped box or violin plot (Section 7.1, 7.2)
Numeric vs. categorical, compare group averagesBar plot of means with error bars (Section 7.3)
Two categorical variablesHeatmap of cross-tabulation (Section 8.1), or stacked/grouped bar chart (Section 8.2)
Three or more variablesHue/size encoding (Section 9.1, 9.2) for up to 3-4 variables, or faceting (Section 9.3) for more, or when clarity matters more than density

11. Common Chart Design Mistakes#

11.1 Truncated Y-Axes Exaggerating Differences#

Starting a bar chart's y-axis at a value other than zero can dramatically and misleadingly exaggerate the visual difference between bars, even when the underlying numeric difference is small:

Python
fig, axes = plt.subplots(1, 2, figsize=(12, 5)) categories = ["A", "B"] values = [48, 52] axes[0].bar(categories, values) axes[0].set_ylim(0, 60) axes[0].set_title("Honest: y-axis starts at 0") axes[1].bar(categories, values) axes[1].set_ylim(45, 55) axes[1].set_title("Misleading: y-axis truncated") plt.tight_layout() plt.show()

The right-hand plot makes a difference of 4 (roughly 8 percent) look like Group B has more than double Group A's value, purely due to the truncated axis; this is one of the most common and most consequential chart design mistakes, whether introduced deliberately or accidentally, and bar charts in particular should almost always start at zero.

11.2 Misleading Dual Axes#

Plotting two variables with very different scales on a single chart using two separate y-axes can create the visual impression of a relationship (two lines crossing, or moving together) that is an artifact of arbitrary axis scaling choices rather than a genuine, meaningful pattern in the underlying data; this technique should be used with extreme caution and, when used, should be clearly and prominently labeled to avoid misleading the viewer.

11.3 Overuse of 3D Effects#

3D bar charts, 3D pie charts, and similar decorative 3D effects generally make a chart harder, not easier, to read accurately, since perspective distortion makes it genuinely more difficult to compare bar heights or slice sizes precisely; flat, 2D representations are almost always the better choice for accurate quantitative comparison, with true 3D visualization reserved for genuinely three-dimensional spatial data.

11.4 Too Many Categories in a Single Chart#

A bar chart or pie chart with 30 or more categories becomes difficult to read and compare meaningfully; grouping smaller categories into an "Other" bucket, or switching to a different visualization approach entirely (such as a table for precise values, or a treemap for hierarchical proportions), is often more effective than forcing every individual category onto one crowded chart.


12. Hands-On: A Complete Visual EDA Pass#

This example builds a small, systematic dashboard of plots covering univariate, bivariate, and multivariate perspectives on a single dataset, directly extending Notebook 7's numeric EDA checklist (Section 9) into its visual counterpart.

Python
import pandas as pd import numpy as np import seaborn as sns import matplotlib.pyplot as plt rng = np.random.default_rng(42) n = 800 df = pd.DataFrame({ "region": rng.choice(["North", "South", "East", "West"], size=n), "tier": rng.choice(["Basic", "Premium", "Pro"], size=n, p=[0.5, 0.35, 0.15]), "account_age_months": rng.integers(1, 60, size=n), "monthly_spend": np.round(rng.exponential(scale=40, size=n), 2) }) sns.set_theme(style="whitegrid") fig, axes = plt.subplots(2, 2, figsize=(14, 10)) # Univariate: distribution of monthly spend sns.histplot(data=df, x="monthly_spend", bins=30, kde=True, ax=axes[0, 0]) axes[0, 0].set_title("Distribution of Monthly Spend") # Univariate: category counts sns.countplot(data=df, x="tier", order=df["tier"].value_counts().index, ax=axes[0, 1]) axes[0, 1].set_title("Customer Count by Tier") # Bivariate: numeric vs categorical sns.boxplot(data=df, x="tier", y="monthly_spend", ax=axes[1, 0]) axes[1, 0].set_title("Monthly Spend by Tier") # Bivariate: two categorical, via heatmap crosstab = pd.crosstab(df["region"], df["tier"]) sns.heatmap(crosstab, annot=True, fmt="d", cmap="Blues", ax=axes[1, 1]) axes[1, 1].set_title("Region vs. Tier") plt.tight_layout() plt.show()

This four-panel layout mirrors the systematic EDA checklist from Notebook 7, Section 9, giving a compact, single-glance visual overview covering: one numeric distribution, one categorical distribution, one numeric-by-categorical comparison, and one categorical-by-categorical relationship, a strong default starting layout for the visual counterpart of any new dataset's exploration.


13. Case Studies#

13.1 Case Study: A KDE Overlay Revealing a Hidden Bimodal Pattern#

An analyst reviewing only the summary statistics table for "session duration" (mean, median, std, skew) sees numbers that look broadly consistent with a moderately right-skewed, single-peaked distribution, similar to many other metrics in the dataset. Plotting a histogram with a KDE overlay (Section 4.1) reveals two distinct peaks: a large cluster of very short sessions (under 1 minute, likely accidental visits or bounces) and a separate cluster of substantive sessions (5-15 minutes).

Diagnosis: Summary statistics like mean, median, and even skewness can look unremarkable for a distribution that is actually bimodal, since these single-number summaries do not directly capture the number of modes; only a visual density representation reliably reveals this, directly echoing the Anscombe's Quartet lesson from Section 1.

Lesson: Treating the two peaks as one undifferentiated population would obscure a genuinely important business distinction (bounces versus real engagement), which likely warrants being handled as two separate segments (per Notebook 7, Section 7.3) in any further analysis, a discovery made possible specifically by visualizing the distribution rather than relying on summary statistics alone.

13.2 Case Study: A Truncated Y-Axis in an Internal Report Overstating a Improvement#

An internal quarterly report shows a bar chart with a y-axis starting at 85 rather than 0, visually suggesting a dramatic jump in a customer satisfaction score from 87 to 91, a small (roughly 4.5 percent relative) numeric change made to look like a doubling of the bar's height. Leadership approves a substantial follow-up budget based partly on the visual impression of this chart.

Diagnosis: This is a direct, real-world instance of the truncated-axis problem described in Section 11.1; whether introduced deliberately or through a default charting tool setting, the effect on the viewer is the same: the chart visually communicates a substantially larger improvement than the underlying data actually shows.

Lesson: Any bar chart intended to communicate magnitude accurately should start its axis at zero by default, and any exception (such as a legitimate need to show fine detail in a narrow, already-well-understood range) should be prominently and explicitly labeled so viewers are not misled about the true scale of a difference.

13.3 Case Study: A Scatter Plot Exposing a Data Entry Error Invisible in Summary Statistics#

A dataset's summary statistics for "customer age" look entirely reasonable (mean around 35, standard deviation around 12, no extreme values flagged by a simple range check). A scatter plot of age against account creation date, built as part of a broader multivariate EDA pass (Section 9), reveals a small but visually obvious cluster of points where the recorded age is impossibly low relative to account tenure (e.g., an age of 8 for an account that has existed for over 20 years).

Diagnosis: Individually, neither the age column nor the tenure column looked unusual in isolation (a univariate check, Notebook 7, Section 3.1, would not have flagged either), but the combination of the two, visible only in a bivariate scatter plot, revealed a data entry or system integration error that a purely univariate or purely numeric-summary-based EDA pass had missed entirely.

Lesson: This directly reinforces the core theme of Section 1: some data quality issues and structural patterns are visible only in a bivariate or multivariate plot and are effectively invisible to univariate summary statistics alone, which is why visual EDA should be treated as a required complement to, not a replacement for or lower priority than, the numeric EDA techniques from Notebook 7.


14. Common Misconceptions and Pitfalls#

MisconceptionReality
"If the summary statistics look normal, I don't need to plot the data."Anscombe's Quartet (Section 1) and the bimodal session duration case study (Section 13.1) both demonstrate that very different underlying structures can produce nearly identical summary statistics.
"A pie chart is the natural choice whenever I have percentages to show."Bar charts are generally easier for viewers to compare accurately, per Section 5.2, and are the better default for most categorical proportion visualizations.
"Starting a bar chart's y-axis somewhere other than zero is a harmless stylistic choice."It can dramatically and misleadingly exaggerate perceived differences, per Section 11.1 and the case study in Section 13.2, and should be avoided by default.
"Adding more variables to a single plot via color, size, and shape always makes a chart more informative."Beyond two or three simultaneously encoded variables, a plot typically becomes harder, not easier, to read; faceting (Section 9.3) is often the clearer alternative for additional dimensions.
"3D charts look more impressive and are therefore better for presentations."3D perspective effects generally reduce a viewer's ability to accurately compare quantities, per Section 11.3, and should generally be avoided for standard quantitative comparisons.
"A box plot fully captures a distribution's shape, since it shows the median and quartiles."A box plot cannot reveal multiple modes (bimodality) the way a histogram or violin plot can, per Section 4.5 and the case study in Section 13.1.

15. Glossary#

TermDefinition
Anscombe's QuartetFour datasets sharing nearly identical summary statistics but strikingly different visual structure, demonstrating the necessity of visualization
Figure / AxesMatplotlib's core objects: the overall canvas (Figure) and individual plot areas within it (Axes)
Kernel Density Estimate (KDE)A smoothed curve estimating the underlying continuous probability density of a variable
OverplottingA visual problem where too many overlapping points make a scatter plot difficult to interpret
Faceting / small multiplesCreating a grid of similar subplots, each showing a different data subset, to visualize additional dimensions clearly
Hue encodingMapping a variable to color within a plot to represent an additional dimension
Truncated axisA chart axis that does not start at zero (or another meaningful baseline), which can visually exaggerate differences
Bimodal distributionA distribution with two distinct peaks (modes), often indicating two underlying sub-populations

16. Exercises#

Exercise 1 (Conceptual)#

Explain, using Anscombe's Quartet (Section 1), why a report that presents only a table of means, medians, and correlation coefficients could be dangerously incomplete, even if every number in the table is computed correctly.

<details> <summary>Solution</summary>

Anscombe's Quartet demonstrates that datasets with nearly identical means, variances, and correlation coefficients can have completely different underlying structures: one might be a genuine linear relationship, another a clear curve, another dominated by a single outlier, and another an otherwise-perfect relationship with one anomalous point. A table of correctly computed summary statistics provides no way to distinguish between these very different scenarios, since the numbers themselves are, by construction, nearly identical across all four cases. Only a visualization (a scatter plot, in this specific example) reliably reveals which of these structurally different situations is actually present, which is why relying solely on numeric summaries, however accurately computed, risks missing critical structural information, exactly as demonstrated by the customer age/tenure scatter plot in Section 13.3.

</details>

Exercise 2 (Conceptual)#

A colleague proposes a bar chart of quarterly revenue with the y-axis starting at 900,000 (rather than 0) so that "the differences between quarters are easier to see." Explain the risk with this approach and suggest an alternative.

<details> <summary>Solution</summary>

Starting the y-axis at 900,000 rather than 0 will visually exaggerate the differences between quarters relative to their true proportional size, potentially making a modest, unremarkable fluctuation look like a dramatic swing, exactly the mechanism described in Section 11.1 and the customer satisfaction case study in Section 13.2. A better alternative is to keep the y-axis starting at zero for the primary bar chart (preserving an honest sense of proportional magnitude), and if finer detail among close values is genuinely needed, to use a separate, clearly and explicitly labeled zoomed-in view (for example, a line chart showing percentage change quarter-over-quarter) rather than silently truncating the bar chart's own axis, since a viewer's default expectation for a bar chart is that height is proportional to value starting from zero.

</details>

Exercise 3 (Coding)#

Given a DataFrame df with columns department (categorical) and salary (numeric), write code producing a single figure with two side-by-side subplots: a box plot of salary by department on the left, and a violin plot of salary by department on the right.

<details> <summary>Solution</summary>
Python
import seaborn as sns import matplotlib.pyplot as plt import pandas as pd df = pd.DataFrame({ "department": ["Sales", "Sales", "Engineering", "Engineering", "Sales", "Engineering"] * 10, "salary": [55000, 78000, 65000, 98000, 82000, 68000] * 10 }) fig, axes = plt.subplots(1, 2, figsize=(14, 6)) sns.boxplot(data=df, x="department", y="salary", ax=axes[0]) axes[0].set_title("Salary by Department (Box Plot)") sns.violinplot(data=df, x="department", y="salary", ax=axes[1]) axes[1].set_title("Salary by Department (Violin Plot)") plt.tight_layout() plt.show()

This directly follows the multi-subplot pattern from Section 2.2 and the box-versus-violin comparison from Section 4.5, allowing a side-by-side visual comparison of the two plot types on the exact same underlying data.

</details>

Exercise 4 (Applied Thinking)#

You are exploring a dataset with a numeric variable (response_time), a categorical variable (server_region, 5 categories), and a second categorical variable (request_type, 20 categories). Describe which chart(s) from this notebook's framework (Section 10) you would use to explore response time across both categorical dimensions simultaneously, and explain any concern with directly visualizing all 20 request_type categories at once.

<details> <summary>Solution</summary>

For examining response_time across server_region (5 categories, a manageable number) and request_type (20 categories, a large number) simultaneously, faceting (Section 9.3) is a strong choice: a FacetGrid with one panel per server_region (5 panels, a reasonable grid size), each showing a box plot or violin plot (Section 7.1, 7.2) of response_time broken down by request_type within that region. The concern with 20 categories on a single axis, per Section 11.4, is that the resulting box or violin plot would become visually crowded and difficult to read accurately; a reasonable mitigation is to first identify, using the numeric groupby techniques from Notebook 7, Section 6.1, the handful of request_type categories with the highest volume or the most extreme response times, and focus the detailed visual comparison on those, potentially grouping the remaining lower-volume categories into an "Other" bucket for the visualization, consistent with the "too many categories" guidance in Section 11.4.

</details>

17. Quiz#

Q1. What does Anscombe's Quartet demonstrate? a) Correlation always implies causation b) Datasets with nearly identical summary statistics can have dramatically different underlying structures, visible only through plotting c) Histograms are always better than scatter plots d) Bar charts should never be used

Q2. Which plot type can reveal a bimodal (two-peaked) distribution that a box plot alone cannot show? a) Bar chart b) Box plot c) Histogram or violin plot d) Heatmap

Q3. Why is starting a bar chart's y-axis at a value other than zero generally considered a design mistake? a) It makes the chart take up less space b) It can visually exaggerate the apparent difference between bars relative to their true proportional size c) It is a technical limitation that cannot be avoided d) It has no effect on how the chart is perceived

Q4. What is the primary purpose of faceting (small multiples)? a) To combine unrelated datasets into a single chart b) To create a grid of similar subplots, each showing a data subset, making additional dimensions easier to compare than heavy color/size encoding c) To replace the need for any summary statistics d) To convert categorical data into numeric data

Q5. True or False: A heatmap of a cross-tabulation is generally a strong choice for visualizing the relationship between two categorical variables.

<details> <summary>Answers</summary>
  1. b) Datasets with nearly identical summary statistics can have dramatically different underlying structures, visible only through plotting
  2. c) Histogram or violin plot
  3. b) It can visually exaggerate the apparent difference between bars relative to their true proportional size
  4. b) To create a grid of similar subplots, each showing a data subset, making additional dimensions easier to compare than heavy color/size encoding
  5. True — per Section 8.1, a heatmap combines precise numeric annotation with color-based pattern recognition, making it one of the strongest tools for two categorical variables
</details>

18. Summary#

  • Visualization is a required complement to numeric summary statistics, not an optional add-on, since Anscombe's Quartet and several case studies in this notebook demonstrate that very different data structures can share nearly identical summary statistics.
  • Matplotlib provides the underlying Figure/Axes architecture, while Seaborn provides a higher-level, DataFrame-integrated, statistically-aware layer that is generally more efficient for EDA work.
  • Chart type should be chosen based on the number and type (numeric or categorical) of variables involved, following a systematic framework: histograms and box plots for single numeric variables, bar charts for single categorical variables, scatter plots for two numeric variables, grouped box/violin plots for numeric-by-categorical, heatmaps for two categorical variables, and hue encoding or faceting for three or more variables.
  • Common design mistakes, particularly truncated y-axes, misleading dual axes, unnecessary 3D effects, and overcrowded categorical axes, can turn an accurate underlying analysis into a misleading visual presentation.
  • A systematic multi-panel visual EDA pass, mirroring the numeric EDA checklist from Notebook 7, provides an efficient, comprehensive first look at any new dataset.

19. Further Reading#

  • Edward Tufte, The Visual Display of Quantitative Information — the foundational text on graphical excellence and avoiding misleading visualizations
  • Seaborn official documentation: https://seaborn.pydata.org/
  • Matplotlib official documentation: https://matplotlib.org/stable/index.html
  • Claus Wilke, Fundamentals of Data Visualization (available free online) — a modern, practical guide to chart selection and design

Next Up#

Notebook 9: Correlation, Covariance, and Relationships — Pearson and Spearman correlation, covariance, correlation matrices, and the correlation-versus-causation distinction, building directly on the scatter plots and pair plots introduced in this notebook to quantify the relationships they reveal visually.


Course: Data Analytics | Notebook 8 of 13

Knowledge Checkpoint

Data Visualization for EDA Checkpoint

Q1.In Matplotlib's Object-Oriented API, what is the conceptual difference between the `Figure` and the `Axes`?
AThe `Figure` is the entire canvas/window containing all elements; the `Axes` is an individual subplot/plot area with its own coordinate system, x/y axes, and labels.
BThe `Figure` is for 3D plots; `Axes` is for 2D plots.
CThe `Axes` is only for line charts.
DThere is no difference.
Q2.What visualization advantage does a Violin Plot have over a standard Box Plot?
AIt combines a box plot with a rotated Kernel Density Estimate (KDE) on each side, revealing multi-modal distributions (e.g. bimodal peaks) that box plots hide.
BIt uses less GPU memory.
CIt only plots positive numbers.
DIt automatically fixes missing values.
Q3.Why should truncated y-axes (starting at non-zero values) be avoided in standard bar charts?
ABar charts encode values by height/length; truncating the baseline exaggerates minor differences and visually distorts the relative proportions of data.
BMatplotlib will raise a RuntimeWarning.
CIt converts bar charts into scatter plots.
DBar charts require logarithmic scales.
Track Your Learning

Finished studying this notebook?

Mark this guide as completed to update your course progress roadmap.