Charts with Matplotlib
Turning a table into a picture
In this lesson
A table of numbers hides its own shape. “The average score is 74” is equally true of a class where everyone scored 74 and a class where half scored 50 and half scored 98 — and those are completely different classes.
Matplotlib turns the table into a picture so the shape becomes obvious. The charts you make here appear right under the code, so you can change a color or a label and see the result immediately.
Explain it like I’m 5
A chart turns a table into a picture, so patterns you would never spot in a column of numbers jump straight out.
Your first chart
The convention is import matplotlib.pyplot as plt. You build a chart by calling functions in order: create a figure, draw something, label it, and finish with plt.show().
Three lines make a chart. The other three make it honest: a title and both axis labels, so someone who did not write the code can tell what they are looking at.
import matplotlib.pyplot as plt
months = ["Jan", "Feb", "Mar", "Apr", "May"]
sales = [120, 145, 133, 178, 162]
plt.figure(figsize=(6, 3.5))
plt.bar(months, sales, color="#2b7cd3")
plt.title("Monthly sales")
plt.xlabel("month")
plt.ylabel("units sold")
plt.show()
print("months:", len(months))
print("best:", months[sales.index(max(sales))])
months: 5 best: Apr
Four chart types, four different questions
Choosing the chart type is the skill. Each one answers a different question, and using the wrong one hides the very thing you were looking for.
- Bar —
plt.bar()— compares separate categories. How do these months compare? - Line —
plt.plot()— shows change over a continuous run, usually time. Which way is this trending? - Scatter —
plt.scatter()— shows the relationship between two numbers. Does more revision go with a higher score? - Histogram —
plt.hist()— shows the distribution of one set of numbers. Are the scores clustered or spread out?
The histogram is the one people skip and the one that would have saved them. It is the chart that answers “is the average lying to me?”, because it shows whether the values bunch around the middle or split into two camps.
Reading charts honestly
A chart is an argument, and it is easy to make it say more than the data supports.
Watch the y-axis. A bar chart starting at 140 rather than 0 makes a 3% difference look enormous. This is the most common way charts mislead, and it is often done without meaning to.
Do not over-read small differences. With five data points, the tallest bar might be tallest by chance. Ask whether the gap would survive more data.
A relationship is not a cause. A scatter showing revision hours rising with exam scores does not prove revision caused the scores. Motivated students may do both. The chart shows the pattern; explaining it is a separate and harder job.
Make a labeled bar chart of how much fruit was sold. Draw the bars from fruit and counts, then add a title, an x-axis label, and a y-axis label so the chart stands on its own. The chart appears under the output when you run it.
import matplotlib.pyplot as plt
fruit = ["apples", "bananas", "cherries", "dates"]
counts = [23, 41, 12, 8]
plt.figure(figsize=(6, 3.5))
# TODO: draw a bar chart of counts against fruit
# TODO: add a title, an x label and a y label
plt.show()
print("most popular:", fruit[counts.index(max(counts))])
print("total sold:", sum(counts))
plt.bar(fruit, counts) draws it. Then plt.title("Fruit sold this week"), plt.xlabel("fruit") and plt.ylabel("number sold"). The printed lines are checked; the chart is shown for you to look at.
import matplotlib.pyplot as plt
fruit = ["apples", "bananas", "cherries", "dates"]
counts = [23, 41, 12, 8]
plt.figure(figsize=(6, 3.5))
plt.bar(fruit, counts, color="#2b7cd3")
plt.title("Fruit sold this week")
plt.xlabel("fruit")
plt.ylabel("number sold")
plt.show()
print("most popular:", fruit[counts.index(max(counts))])
print("total sold:", sum(counts))
most popular: bananas
total sold: 84
Common mistake: Leaving the chart unlabeled
You know what it shows while you are making it.
Add a title and both axis labels every time. It is three lines and it is the difference between a chart and a decoration.
Common mistake: Choosing a chart type that hides the answer
Bar and line charts are the familiar ones, so they get used for everything.
Match the chart to the question: bar compares categories, line shows change over time, scatter shows relationships, histogram shows spread.
Common mistake: Reading too much into a tiny difference
The eye is drawn to the tallest bar regardless of the scale.
Check where the y-axis starts, and ask whether the gap would survive more data.
Common mistake: Plotting text where numbers are needed
A column read from CSV can be text that looks numeric.
Check dtypes and convert first, or the bars come out in the wrong order or not at all.
Which chart compares separate categories?
Bars compare categories side by side. A line between unordered categories implies a progression that is not real.
Why label the axes?
An unlabeled chart shows shapes without saying what they measure.
What does a histogram show?
It reveals whether values cluster around the middle or split into groups, the thing an average hides.
Mini exercise (medium)
Eight students, their revision hours and their exam scores. A bar chart is the wrong tool here. The question is whether the two numbers move together, so use a scatter plot. Draw it with plt.scatter(), label both axes and give it a title.
Try it yourself. Complete the snippet and hit Run; it executes in your browser, no setup required.
import matplotlib.pyplot as plt
hours = [1, 2, 3, 4, 5, 6, 7, 8]
scores = [52, 55, 61, 64, 70, 72, 79, 85]
plt.figure(figsize=(6, 3.5))
# TODO: draw a scatter plot of scores against hours
# TODO: title it, and label both axes
plt.show()
print("pairs plotted:", len(hours))
print("score range:", min(scores), "to", max(scores))
plt.scatter(hours, scores) takes the x values then the y values. Label the x-axis “hours revised” and the y-axis “exam score”, since a scatter is meaningless without knowing which axis is which.
import matplotlib.pyplot as plt
hours = [1, 2, 3, 4, 5, 6, 7, 8]
scores = [52, 55, 61, 64, 70, 72, 79, 85]
plt.figure(figsize=(6, 3.5))
plt.scatter(hours, scores, color="#2b7cd3")
plt.title("Revision hours vs exam score")
plt.xlabel("hours revised")
plt.ylabel("exam score")
plt.show()
print("pairs plotted:", len(hours))
print("score range:", min(scores), "to", max(scores))
pairs plotted: 8
score range: 52 to 85
assert "scatter" in __user_code__, "use plt.scatter for a relationship between two numbers"
assert "xlabel" in __user_code__ and "ylabel" in __user_code__, "label both axes"
assert "title" in __user_code__, "give the chart a title"
print("✓ Looks good!")