7.2 Line, Bar, Scatter, and Distribution Plots
The object model tells you where to draw. The analytical question tells you what to draw. A chart type is a claim about structure: a line claims adjacency, a bar compares lengths from a baseline, a scatter plot positions paired values, and a histogram groups a continuous variable into intervals.
Match the encoding to the comparison
Use a line plot for an ordered sequence where connecting adjacent observations has meaning, most commonly time:
daily = daily.sort_values("date")
fig, ax = plt.subplots(layout="constrained")
ax.plot(daily["date"], daily["orders"], marker="o")
ax.set(
title="Daily completed orders",
xlabel="Date",
ylabel="Orders (count)",
)Sort the ordering variable first. A line connected in arbitrary row order invents a path. Also decide what a missing date means. Dropping it connects the neighboring dates directly, which can imply continuity across an outage. Depending on the process, you may reindex to a complete calendar and leave a gap, fill a genuine zero, or annotate missing collection.
Use a bar plot to compare categories through length on a shared baseline:
region_sales = (
orders.groupby("region")["amount"]
.sum()
.sort_values()
)
fig, ax = plt.subplots(layout="constrained")
ax.barh(region_sales.index, region_sales.values)
ax.set(
title="Completed sales by region",
xlabel="Sales (USD)",
ylabel="Region",
xlim=(0, None),
)Bars normally begin at zero because their length encodes magnitude. Truncating the baseline exaggerates differences. A dot plot can be a better choice when a nonzero reference or compact comparison matters.
Use a scatter plot for paired continuous variables:
fig, ax = plt.subplots(layout="constrained")
ax.scatter(
orders["amount"],
orders["delivery_minutes"],
alpha=0.55,
)
ax.set(
title="Order value and delivery time",
xlabel="Order value (USD)",
ylabel="Delivery time (minutes)",
)Each point represents one observation. Do not connect arbitrary observations with a line: that adds an unsupported order. With many overlapping points, reduce marker size and opacity, use a hexbin or two-dimensional histogram, or show a representative sample while reporting the sampling rule.
Visual association is not causation. A trend in the cloud may reflect a third variable, selection process, or shared time pattern. Chapter 8 will separate exploratory evidence from predictive evaluation.
Inspect distributions through more than one lens
A histogram partitions a continuous variable into bins and draws count or density per interval:
values = deliveries["minutes"].dropna()
fig, ax = plt.subplots(layout="constrained")
counts, edges, patches = ax.hist(
values,
bins="fd",
edgecolor="white",
)
ax.set(
title="Delivery-time distribution",
xlabel="Delivery time (minutes)",
ylabel="Orders (count)",
)The returned edges reveal the actual intervals. Bin choice is analytical: wide bins can hide modes or gaps, while narrow bins can turn sampling noise into spikes. The Freedman–Diaconis rule uses
as a data-dependent bin width , but it is a starting point rather than a truth guarantee. Compare several defensible widths with identical x-limits.
An empirical cumulative distribution function (ECDF) avoids bins. At each observed value , it displays the fraction of observations less than or equal to :
Construct it directly when compatibility matters:
import numpy as np
sorted_values = np.sort(values.to_numpy())
cumulative = np.arange(1, len(sorted_values) + 1) / len(sorted_values)
ax.step(sorted_values, cumulative, where="post")An ECDF makes percentiles and tail proportions easy to read, though clusters may be less visually immediate than in a histogram. A box plot compactly shows median, quartiles, and rule-based whiskers, but it hides distribution shape and sample density. Use complementary views rather than expecting one graphic to answer every distribution question.
Keep verified extreme observations visible or show a clearly labeled sensitivity panel. Silently removing them changes both the domain and the histogram range.
The best chart is the simplest faithful encoding of the current question. Section 7.3 makes that encoding readable and honest through labels, scales, annotations, and uncertainty.