Matching the display to the question, and the two chart types worth avoiding.
Overview
Start from the question
Choosing the right chart type is the most consequential decision in data visualisation. The wrong chart does not just look bad. It hides the pattern, misleads the reader, or answers a question nobody asked.
The decision is simpler than it seems. Most data questions fall into four categories: comparison, trend, distribution and relationship. Each category has a standard chart type. Matching the question to the category picks the chart. The remaining choices -- colour, layout, labels -- are refinements.
This page does not assume you know what your data looks like yet. It starts from the question you want to answer and works toward the chart. That order matters. Starting from the data and asking "what chart should I use?" produces a chart of the data. Starting from a question and asking "what chart answers this?" produces a chart that communicates.
The page also covers the charts that are almost always wrong. Pie charts, dual-axis plots and 3-D bar charts each have specific failure modes that make them misleading in most situations. Knowing why they fail helps you avoid them and recognise them in other people's work.
After the four standard types, the page covers less common but useful charts: small multiples for comparing many groups, slope charts for before-and-after comparisons, heatmaps for dense matrices, and area charts for part-to-whole over time.
The final section covers redesigning a chart. Taking a chart that exists and asking what question it should answer, what chart type fits that question, and what it should look like. That exercise is the fastest way to build chart-selection intuition.
Most charts answer one of four questions, and each has a standard display:
How did this change over time? A line chart. Time on the x axis, left to right.
How do these groups compare? A bar chart. Flat. The labels read. Sorted. So The ranking is direct.
Is there a link between these two things? A scatter plot.
How is this spread? A histogram for one group, box plots or the points themselves for a few.
That covers the large majority of real charts. When none of them fits, it's often because the question hasn't been stated just. Stating it finds the chart.
The corollary: decide the question first. A chart made by plotting the data and then deciding what it shows is often a chart of a few things at once.
Worth knowing
Time is a line, categories are a sorted horizontal bar, relationships are a scatter, spread is a histogram or box.
Pie charts ask readers to compare angles, which they do badly. A sorted bar chart is better in every way beyond two or three slices.
Dual axes put the crossing point under your control rather than the data's; index both series instead.
Small multiples with shared axes beat six series on one chart — the reader compares shapes instead of tracing colours.
Highlighting one series and greying the rest says what the chart is about without a legend.
Before it leaves your screen: title states the finding, units on the labels, right baseline, nothing hidden, no wasted ink, survives greyscale, one message.
Choosing a Chart
Matching the display to the question, and the two chart types worth avoiding.
The question decides the chart
Four common questions, four displays.
example_01.pymatplotlib
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(0)
fig, axes = plt.subplots(2, 2, figsize=(9, 5))
t = np.arange(40)
axes[0, 0].plot(t, rng.normal(0, 1, 40).cumsum())
axes[0, 0].set_title("change over time -> line")
axes[0, 1].barh(list("abcd"), sorted(rng.integers(5, 40, 4)))
axes[0, 1].set_title("compare categories -> bar")
x = rng.normal(size=200)
axes[1, 0].scatter(x, x * 0.6 + rng.normal(size=200), s=12, alpha=0.5)
axes[1, 0].set_title("relationship -> scatter")
axes[1, 1].hist(rng.normal(size=500), bins=25)
axes[1, 1].set_title("distribution -> histogram")
fig.tight_layout()
print("time -> line")
print("categories-> bar, horizontal, sorted")
print("relation -> scatter")
print("spread -> histogram, box or the points themselves")
print()
print("Most charts are one of these four. Reaching for something more")
print("exotic usually means the question has not been stated yet.")
Output
Pie charts, and why not
Angle is the hardest visual encoding to compare.
example_02.pymatplotlib
import matplotlib.pyplot as plt
import numpy as np
vals = np.array([23, 21, 19, 20, 17])
names = ["a", "b", "c", "d", "e"]
fig, (p, q) = plt.subplots(1, 2, figsize=(9, 3.4))
p.pie(vals, labels=names, autopct="%.0f%%")
p.set_title("which is largest?")
order = np.argsort(vals)
q.barh([names[i] for i in order], vals[order])
q.set_title("obvious")
print("values:", dict(zip(names, vals)))
print()
print("Five similar slices are genuinely hard to rank by eye, because")
print("people compare angles badly and areas only slightly better.")
print("The same numbers as a sorted bar chart are trivial.")
print()
print("A pie is defensible for two or three parts of an obvious whole.")
print("Beyond that a bar chart is better in every way.")
Output
Dual axes, restated
Included here because it is the other one worth avoiding.
example_03.pymatplotlib
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(20)
a_vals = np.linspace(10, 90, 20)
b_vals = np.linspace(80, 20, 20)
fig, (p, q) = plt.subplots(1, 2, figsize=(9, 3.2))
p.plot(x, a_vals, color="#264653")
p2 = p.twinx()
p2.plot(x, b_vals, color="#e76f51")
p2.set_ylim(0, 300)
p.set_title("crossing point is arbitrary")
q.plot(x, a_vals / a_vals[0] * 100, color="#264653", label="A")
q.plot(x, b_vals / b_vals[0] * 100, color="#e76f51", label="B")
q.set_ylabel("indexed to start = 100")
q.legend()
q.set_title("one honest axis")
print("Changing the right-hand limits moves where the lines cross,")
print("and readers treat the crossing as a finding.")
print()
print("Indexing both to a common base gives one axis and a crossing")
print("that means something: relative growth diverged.")
Output
Small multiples beat one busy chart
Seven series on one axes is a hairball; seven panels is a comparison.
example_04.pymatplotlib
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(1)
series = {name: rng.normal(0, 1, 60).cumsum() + 20
for name in ["a", "b", "c", "d", "e", "f"]}
fig, ax = plt.subplots(figsize=(6.5, 3))
for name, y in series.items():
ax.plot(y, label=name)
ax.legend(ncol=3, fontsize=8)
ax.set_title("six series, one axes")
fig2, axes = plt.subplots(2, 3, figsize=(8, 3.6), sharex=True, sharey=True)
for ax2, (name, y) in zip(axes.flat, series.items()):
ax2.plot(y, color="#264653")
ax2.set_title(name, fontsize=9)
fig2.suptitle("the same data as small multiples")
fig2.tight_layout()
print("The first chart makes you trace six colours through a legend.")
print("The second lets you see each shape and compare across panels,")
print("because sharey means the heights mean the same thing.")
Output
Highlight rather than colour everything
One series in colour, the rest in grey, says what the chart is about.
example_05.pymatplotlib
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(2)
names = ["a", "b", "c", "d", "e", "f", "g"]
series = {n: rng.normal(0, 1, 50).cumsum() + 20 for n in names}
focus = "d"
fig, (p, q) = plt.subplots(1, 2, figsize=(10, 3.2), sharey=True)
for n, y in series.items():
p.plot(y, label=n)
p.legend(ncol=4, fontsize=7)
p.set_title("all seven equally loud")
for n, y in series.items():
if n == focus:
continue
q.plot(y, color="0.8", linewidth=1)
y = series[focus]
q.plot(y, color="crimson", linewidth=2.5)
q.text(len(y) - 1, y[-1], " " + focus, color="crimson", va="center")
q.set_title("one highlighted, the rest as context")
print("The grey lines still provide the range for comparison.")
print("The coloured one is the subject. Nobody has to read a legend.")
Output
The checklist
What to look at before a chart leaves your screen.
example_06.pymatplotlib
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(3)
months = np.arange(1, 13)
y = np.array([12, 15, 14, 18, 22, 25, 24, 27, 23, 20, 18, 21])
fig, ax = plt.subplots(figsize=(7, 3.5))
ax.plot(months, y, marker="o", color="#264653", linewidth=2)
i = y.argmax()
ax.plot(months[i], y[i], marker="o", color="crimson", markersize=10)
ax.set_title("Sales peaked in August, then fell 22%",
loc="left", fontsize=12, fontweight="bold")
ax.set_xlabel("Month of 2024")
ax.set_ylabel("Sales (thousands)")
ax.set_ylim(0, 30)
ax.grid(True, axis="y", alpha=0.3)
ax.set_axisbelow(True)
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
for line in [
"1. does the title say the finding, not the variables?",
"2. do the axis labels carry units?",
"3. is the baseline right - zero for bars?",
"4. is anything hidden by overplotting?",
"5. is the ink that is not data doing any work?",
"6. would it survive greyscale?",
"7. is there one message, or several fighting?",
]:
print(line)
Output
Before a chart leaves your screen:
Does the title state the finding, rather than naming the vars?
Do the axis labels carry units?
Is the baseline right — zero for bars. Does a truncated line axis need saying?
Is anything hidden — overplotted points, a series behind another, a group off the edge?
Is the non-data ink doing work. The grid, the frame, the legend, the tick marks?
Would it live greyscale, and colour-vision deficiency?
Is there one message, or a few competing? Two charts each saying one thing beat one saying both.
None of these is about matplotlib. This Is the point. The library will draw what you ask. The choices are yours. They're what makes a chart worth looking at.
Why encodings matter
Some visual channels are read more accurately than others. Roughly, in order:
spot along a common scale. The most accurate. This is what a line, a scatter and a dot plot use.
Length from a common baseline — nearly as good. Bars.
Angle and area — noticeably worse. Pies, bubbles.
Colour intensity — worse again, and good only for sorting, not for reading values.
That sorting is why a bar chart beats a pie chart, why bubble size should never carry the main message. Why a heatmap needs its numbers written in when the values matter.
Pie charts
A pie asks the reader to compare angles. With two or three slices of an obvious whole, that is fine. The shape sends "parts of a total" at once.
With five similar slices it fails: ranking them by eye is truly hard. This Is why almost each pie chart in the wild has its percentages printed on it. An admission that the picture isn't doing the work.
The same numbers as a sorted flat bar chart are trivially readable. The bar chart also shows the magnitudes rather than only the proportions.
Donut charts are pies with the middle removed. This Makes them slightly worse. The angle is now inferred from an arc.
Dual axes
The other display worth avoiding, covered fully in its own module.
The short version: two series on split scales have no defined link. Where they cross is found by the limits you chose. Readers see the crossing as a finding.
Stacked panels sharing an x axis, or indexing both series to a common base, say the same thing without inventing a fact.
Small multiples
Six series on one axes is a hairball with a legend. The reader traces each colour, loses it in a crossing, and consults the legend again and again.
Six small panels, sharing axes, let them see each shape right and compare across panels. The shared scale is what makes the compare valid. The panel title replaces the legend.
This is one of the most solid gains on hand. Matplotlib makes it one call: plt.subplots(2, 3, sharex=True, sharey=True).
The trade-off is that checking two exact series is harder when they're in other panels. If the chart is about one compare, put those two on one axes. If it's about the set, use multiples.
Highlighting
When one series is the subject and the others are context, say so with colour. The subject in a strong colour and heavier line, the rest in light grey.
The grey lines still give the range and the shape of the group. Nothing is lost. The reader's eye goes to the subject at once. No legend is needed if the highlighted line is labelled at its end.
This is more good than an annotation. It directs focus by contrast rather than by adding another thing to read.
Ranking, not just comparing
"Compare groups" splits into two questions that want other charts.
Which is biggest? A sorted flat bar chart, and nothing else comes close.
How does each compare with a link? A dot plot against a line, or a bar of the gap from the link. This Puts the compare on the axis instead of asking the reader to subtract.
The second is under-used. A chart of "actual minus target" answers the question right. Here Two bars per group makes the reader do the math.
Part-to-whole
Four displays, in falling order of how well they work.
Stacked bar normed to 100%. Close across groups. Readable for four or five parts.
A single stacked bar — fine for one composition with few parts.
Small multiples of the parts. Better when the single trends matter more than the composition.
A pie — two or three parts. Only when "these are shares of one thing" is the message rather than the values.
Treemaps handle many parts and are hard to compare just. They suit tree more than proportion.
Distribution over time
A frequent question with no single good answer.
A line of the median with a shaded interquartile band works well and shows the middle and the spread.
A box plot per period works when the periods are few.
A heatmap of time against value bins shows the full spread and takes practice to read.
Single lines, greyed — a spaghetti plot with a highlighted median — works when the number of entities is modest and their single paths matter.
The wrong answer is a line of the mean alone. This Hides all that changed about the spread.
Two variables plus a third
A scatter with colour is the first choice. A scatter with size is second and much weaker.
If the third var is cat with few levels, small multiples. One scatter per level, shared axes. Beat colour. Checking panels is easier than separating shared colours.
If it's smooth, colour with a sequential map and a colorbar.
Encoding four vars on one chart is often a mistake. The fourth turns decoration. The chart takes longer to read than two charts would.
The general rule
each chart makes the reader do some work. The choice of chart decides how much.
A sorted bar chart asks them to read a ranking that is already visible. An unsorted one asks them to sort. A pie asks them to compare angles. A dual-axis chart asks them to hold two scales at once. A twelve-line chart asks them to trace colours through a legend.
The best chart is often the one that has already done the work. Sorted, filtered, highlighted, labelled. Leaving the reader only the conclusion.
Charts for a specific audience
The same data justifies other charts based on who is reading.
For yourself, exploring — density, defaults, many panels. Nothing needs labelling because you know what it's. Speed matters more than polish.
For a colleague — labelled, titled, one message per chart, and the caveats visible.
For a presentation — one chart, one message, large text, minimal link material. The conclusion in the title. The reader has seconds and can't zoom.
For a doc — sized for the column, with a caption carrying the detail that would clutter the chart.
The mistake is showing an exploratory chart to an audience. This Is how a chart with eleven series and no title ends up in a meeting.
The one-message rule
The most handy constraint in charting is that a chart should say one thing.
It forces the choices: which var is on which axis, what to highlight, what to leave out, what the title claims.
A chart trying to say three things often says none. The reader can't tell which of the three to look at. The elements supporting each compete with the others.
Two charts each saying one thing take the same space and send more. Splitting is nearly always the right answer when a chart feels crowded. "what is this chart for?" is the question that resolves most design args about it.
Redesigning a chart
A hands-on exercise: take a chart that isn't working and improve it without changing the data.
The sequence that often helps, in order:
State the message in a sentence. If that is hard, the chart is trying to do more than one thing.
Drop all the sentence doesn't need. Series, gridlines, the legend if direct labels would do, tick marks, the box around the plot.
Sort, if the order is any.
Highlight the subject and grey the rest.
Put the sentence in the title.
Label the thing the sentence refers to.
Six steps, none of which needs new data. Together they turn a chart that shows the numbers into one that makes the point. Most charts improve much and the code gets shorter. Most of the steps drop a thing.
Choosing a Chart, in summary
Time is a line, groups are a sorted flat bar, links are a scatter, spreads are a histogram or a box.
spot is read most accurately, then length. Angle and area much less well. That sorting is why bars beat pies and why bubble size should never carry the main message.
Dual axes put the crossing point under your control rather than the data's.
Small multiples beat many series on one chart when the question is about the set. Highlighting beats a legend when it's about one.
And the best chart is the one that has already done the reader's work. Sorted, filtered, labelled, with the conclusion in the title and nothing else competing for focus.
A closing note on Choosing a Chart
all else in this track is method. This module is the part that decides whether the method was worth using.
The recurring theme is that a chart is a piece of exchange. The reader's effort is the cost. Sorting, filtering, highlighting and labelling all move work from the reader to the author. The best charts have had the most work moved.
The chart types worth avoiding. Pies with many slices, dual axes, truncated bars — are all cases where the display asks the reader to do a thing people do badly, or invites a conclusion the data doesn't support.
And the most handy single constraint remains the simplest. One chart, one message. Almost each design question resolves once that is decided.
Questions people ask about Choosing a Chart
How do I choose a chart type? Start with the question. Comparison suits bars, trend suits lines, distribution suits histograms, and relationship suits scatter plots.
When is a pie chart acceptable? For a few parts of one whole, when one slice is clearly the largest. Otherwise bars are easier to compare.
Should I use 3D charts? Rarely. Depth distorts lengths and hides data behind other data. A 2D chart with colour or facets usually works better.
What if no single chart works? Split the question. Two simple charts almost always beat one crowded one.
How many series can one chart hold? About five. Past that, use small multiples or highlight one series against grey.
Does the audience matter? Yes. A specialist reads a box plot easily, while a general audience may need a bar chart with labelled values.
Reading the code back
Choosing is done before any code: what question, what encoding, what to leave out, what the title will claim. A chart written from those four answers is often short. Most of the length in plotting code comes from adjusting a display that wasn't the right one to begin with.
Check yourself
0 of 4
Answer without scrolling back up.
Which visual encoding do people read most accurately?
Length from a common baseline is close behind. Angle and area are noticeably worse, which is why bars beat pies.
Why do almost all pie charts print their percentages?
A sorted horizontal bar chart shows the same data readably, and shows magnitudes rather than only proportions.
When are small multiples better than one chart with six series?
If the chart is about one specific comparison, put those two series on one axes instead.
What is the advantage of greying out all but one series?
More effective than an annotation, because it does not add another thing to read.
Cheat sheet
Choosing a Chart
Match your data question to the right chart type. Walk through a decision tree that picks between bar, line, scatter and histogram.
Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.