Chapter 18 Visualization Fundamentals

A chart should answer a defined question and represent the data honestly. Choose the mark and scale from the analytical task rather than adding visual decoration first.

18.1 Line, scatter, and histogram use cases

  • A line chart emphasizes ordered change, commonly over time.
  • A scatter plot examines the relationship between two numeric variables.
  • A histogram summarizes the distribution of one numeric variable; results depend on the selected bins.

18.2 Build a labeled chart explicitly

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import pandas as pd

monthly = pd.DataFrame(
    {
        "month": pd.to_datetime(["2026-01-01", "2026-02-01", "2026-03-01"]),
        "revenue": [100, 115, 108],
    }
)

figure, axis = plt.subplots(figsize=(6, 3))
axis.plot(monthly["month"], monthly["revenue"], marker="o")
axis.set(
    title="Monthly revenue",
    xlabel="Month",
    ylabel="Revenue ($ thousands)",
)
figure.autofmt_xdate()

assert axis.get_ylabel() == "Revenue ($ thousands)"
plt.close(figure)

Labels should include units. For time plots, sort chronologically and do not connect observations when a continuous line would imply nonexistent data.

18.3 Scatter plots and encoded groups

campaigns = pd.DataFrame(
    {
        "spend": [10, 20, 30, 40],
        "conversions": [8, 18, 25, 31],
        "channel": ["search", "search", "social", "social"],
    }
)

figure, axis = plt.subplots(figsize=(5, 3))

for channel, group in campaigns.groupby("channel"):
    axis.scatter(group["spend"], group["conversions"], label=channel)

axis.set(xlabel="Spend ($ thousands)", ylabel="Conversions")
axis.legend(title="Channel")

assert len(axis.collections) == 2
plt.close(figure)

A visual association does not establish causality. Confounding, selection, and time trends can produce persuasive patterns without a causal effect.

18.4 Histograms and bin sensitivity

values = [1, 2, 2, 3, 3, 3, 8, 9]
figure, axis = plt.subplots(figsize=(5, 3))
counts, bin_edges, _ = axis.hist(values, bins=[0, 2, 4, 6, 8, 10])
axis.set(xlabel="Value", ylabel="Count")

assert counts.sum() == len(values)
assert len(bin_edges) == 6
plt.close(figure)

Inspect alternative bin choices when the apparent shape drives a conclusion.

18.5 Communication checklist

  • What question does the chart answer?
  • Are labels, units, and population clear?
  • Does the axis range distort the comparison?
  • Are color and shape accessible and meaningful?
  • Does aggregation hide important variation?
  • Is uncertainty shown when it matters?
  • Does the wording avoid implying causality from association alone?

18.6 Exercises

  1. Create a bar chart of mean revenue by channel and label the aggregation.
  2. Compare two histogram bin choices for the same values.
  3. Explain when a table communicates exact values better than a chart.