Data Visualization with Matplotlib

Reviewed & published by Brayan K

Matplotlib is Python's most established plotting library. With a handful of pyplot calls you can turn raw numbers into line charts, scatter plots, bar charts, and histograms — and the object-oriented API gives you full control over every figure and axes.

Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

What You'll Learn in This Lesson

📈 1. Your First Plot

import matplotlib.pyplot as plt

x = [1, 2, 3, 4]
y = [1, 4, 9, 16]

plt.plot(x, y)
plt.xlabel("x")
plt.ylabel("y = x squared")
plt.title("My First Plot")
plt.show()
The chart this code draws: plot, labels, title, show
What this code draws, rendered with Matplotlib.

⚠️ Requires pip install matplotlib and a local Python display

🔵 2. Scatter, Bar, and Histogram

Different data calls for different chart types:

import matplotlib.pyplot as plt

# Scatter: relationship between two variables
plt.scatter([1, 2, 3], [3, 1, 2])
plt.show()

# Bar: compare categories
plt.bar(["A", "B", "C"], [5, 7, 3])
plt.show()

# Histogram: distribution of one dataset
plt.hist([1, 1, 2, 3, 3, 3, 4], bins=4)
plt.show()
The chart this code draws: scatter, bar, hist
What this code draws, rendered with Matplotlib.
FunctionBest for
plt.plotTrends over an ordered axis
plt.scatterRelationship between two variables
plt.barComparing categories
plt.histDistribution of one dataset

🏷️ 3. Labels, Titles, and Legends

Annotate plots with the label= argument plus legend():

import matplotlib.pyplot as plt

plt.plot([1, 2, 3], [1, 2, 3], label="growth")
plt.plot([1, 2, 3], [3, 2, 1], label="decline")
plt.title("Two Trends")
plt.xlabel("time")
plt.ylabel("value")
plt.legend()   # shows the labels
plt.show()
The chart this code draws: label= plus plt.legend()
What this code draws, rendered with Matplotlib.

🧩 4. The Object-Oriented API

Create explicit Figure and Axes objects with plt.subplots():

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([1, 2, 3], [2, 4, 6])
ax.set_title("OO API")
ax.set_xlabel("x")
ax.set_ylabel("y")
plt.show()
The chart this code draws: Explicit figure and axes objects
What this code draws, rendered with Matplotlib.

This is the recommended style for anything beyond a quick one-off chart.

🟢 Worked Example: a chart you can check without seeing it

# 🟢 WORKED EXAMPLE — build a two-line chart, save it, and prove what it contains
import matplotlib
matplotlib.use("Agg")          # "Agg" = draw into a file, never try to open a window
import matplotlib.pyplot as plt
from pathlib import Path

months  = ["Jan", "Feb", "Mar", "Apr", "May"]
signups = [120, 150, 145, 190, 240]
cancels = [10, 12, 30, 18, 22]

# One figure, one axes. figsize is in inches: 8 wide by 4.5 tall.
fig, ax = plt.subplots(figsize=(8, 4.5))

# Each ax.plot() call adds one line to the SAME axes. label= is what the legend reads.
ax.plot(months, signups, marker="o", label="sign-ups")
ax.plot(months, cancels, marker="s", linestyle="--", label="cancellations")

ax.set_title("Sign-ups vs cancellations")   # OO API: set_title, not plt.title
ax.set_xlabel("Month")
ax.set_ylabel("People")
ax.legend()                    # without this call the label= values are never shown
ax.grid(True, alpha=0.3)       # alpha=0.3 keeps the grid faint behind the data

fig.tight_layout()             # stops the labels being clipped at the edges
fig.savefig("signups.png", dpi=120)     # the extension picks the format: .png, .pdf, .svg

# ---- proof it worked, in text, because this editor only shows printed output ----
print("lines drawn   :", len(ax.lines))
print("legend labels :", [t.get_text() for t in ax.get_legend().get_texts()])
print("title         :", ax.get_title())
print("axis labels   :", ax.get_xlabel(), "/", ax.get_ylabel())
print("x tick labels :", [t.get_text() for t in ax.get_xticklabels()])
print("net growth    :", [s - c for s, c in zip(signups, cancels)])
print("file written  :", Path("signups.png").exists())

# ✅ Expected output:
# lines drawn   : 2
# legend labels : ['sign-ups', 'cancellations']
# title         : Sign-ups vs cancellations
# axis labels   : Month / People
# x tick labels : ['Jan', 'Feb', 'Mar', 'Apr', 'May']
# net growth    : [110, 138, 115, 172, 218]
# file written  : True
The chart this code draws: Agg backend, savefig, and a printed check of what the chart contains
What this code draws, rendered with Matplotlib.

Everything you drew is still queryable afterwards — ax.lines, ax.get_title(), the legend's texts. A chart is not a picture until you save it; until then it is a Python object you can inspect, tweak and test. That is exactly how you would write an automated test for a plotting function.

🎯 Your Turn: chart a poll result

Three blanks: pick the right chart type for categories, set a title, and label the axis. Use exactly the strings shown in the hints so your output matches the expected output.

# 🎯 YOUR TURN — fill in the blanks marked with ___
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

languages = ["Python", "JS", "Go", "Rust"]
votes     = [42, 35, 12, 18]

fig, ax = plt.subplots(figsize=(6, 4))

# 1) You are comparing four separate categories, not a trend over time.
ax.___(languages, votes, color="steelblue")   # 👉 plot, scatter, bar or hist?

# 2) Give it a title. On an Axes the method is set_something.
ax.___("Favourite language poll")             # 👉 which Axes method sets the title?

# 3) Say what the height of a bar means.
ax.set_ylabel(___)                            # 👉 the text "Votes", quotes included

fig.tight_layout()
fig.savefig("poll.png", dpi=120)

# ---- printed check ----
print("container type:", type(ax.containers[0]).__name__)
print("bars drawn    :", len(ax.containers[0]))
print("title         :", ax.get_title())
print("y label       :", ax.get_ylabel())
print("tallest bar   :", languages[votes.index(max(votes))], max(votes))

# ✅ Expected output:
# container type: BarContainer
# bars drawn    : 4
# title         : Favourite language poll
# y label       : Votes
# tallest bar   : Python 42

If blank 1 goes wrong you will see it immediately: ax.containers is empty for a line chart, so the first print raises IndexError: list index out of range instead of printing BarContainer.

🪟 5. Multiple Subplots

Pass nrows and ncols to get a grid of axes:

import matplotlib.pyplot as plt

fig, axes = plt.subplots(nrows=1, ncols=2)
axes[0].plot([1, 2, 3], [1, 2, 3])
axes[0].set_title("Left")
axes[1].bar(["A", "B"], [3, 5])
axes[1].set_title("Right")
fig.tight_layout()
plt.show()
The chart this code draws: Several panels in one figure
What this code draws, rendered with Matplotlib.

axes is an array you index into; each entry is its own Axes you draw on.

🎨 6. Styling

Control color, marker, and line style:

import matplotlib.pyplot as plt

plt.plot(
    [1, 2, 3], [1, 4, 9],
    color="green",
    linestyle="--",
    marker="o",
    linewidth=2,
)
plt.grid(True)
plt.show()
The chart this code draws: Fine-tune the look
What this code draws, rendered with Matplotlib.

💾 7. Saving Figures

Write the figure to a file with savefig:

import matplotlib.pyplot as plt

plt.plot([1, 2, 3], [1, 2, 3])
plt.title("Saved Chart")

# Format is chosen from the file extension
plt.savefig("chart.png", dpi=150, bbox_inches="tight")
# also works: chart.pdf, chart.svg
The chart this code draws: Export charts to PNG, PDF, SVG
What this code draws, rendered with Matplotlib.

Call savefig before show() — showing can clear the current figure in some backends.

🏁 Mini-Challenge: a two-panel weather figure

No blanks — a brief and an empty outline. Build one figure with two panels side by side: a line chart of temperature on the left, a bar chart of rainfall on the right, each with its own title and y-axis label. Save it as week.png.

Use the exact titles "Temperature" and "Rainfall" so the printed check matches the expected output below.

# 🏁 MINI-CHALLENGE — write it yourself
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from pathlib import Path

days        = ["Mon", "Tue", "Wed", "Thu", "Fri"]
temperature = [12, 14, 13, 17, 19]
rainfall    = [4, 0, 9, 2, 0]

# 1. fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(10, 4))
#    axes is an ARRAY: axes[0] is the left panel, axes[1] is the right one.
# 2. Left panel: a line of temperature against days, marker="o",
#    title "Temperature", y label "degrees C".
# 3. Right panel: bars of rainfall against days,
#    title "Rainfall", y label "mm".
# 4. fig.tight_layout(), then save the figure as week.png at dpi=120.

# your code here


# ---- printed check: leave this at the bottom ----
print("panels       :", len(fig.axes))
print("left title   :", fig.axes[0].get_title())
print("right title  :", fig.axes[1].get_title())
print("left lines   :", len(fig.axes[0].lines))
print("right bars   :", len(fig.axes[1].containers[0]))
print("warmest day  :", days[temperature.index(max(temperature))])
print("file written :", Path("week.png").exists())

# ✅ Expected output:
# panels       : 2
# left title   : Temperature
# right title  : Rainfall
# left lines   : 1
# right bars   : 5
# warmest day  : Fri
# file written : True

🎉 Conclusion

With Matplotlib you can:

✔ Draw line, scatter, bar, and histogram charts

✔ Annotate with labels, titles, and legends

✔ Build multi-panel figures with subplots

✔ Style colors, markers, and grids

✔ Save publication-ready images with savefig

Reach for the fig, ax API whenever a plot grows beyond a quick sketch.

📋 Quick Reference — Matplotlib

SyntaxWhat it does
plt.plot(x, y)Line chart
plt.hist(data, bins=10)Distribution histogram
fig, ax = plt.subplots()Object-oriented figure/axes
ax.set_title('T')Set title on an axes
plt.savefig('chart.png')Export figure to a file

🎉 Great work! You've completed this lesson.

You can now turn data into clear, styled charts and export them for reports.

Practice quiz

Which Matplotlib module provides the familiar plot, scatter, and bar functions?

  • matplotlib.pyplot
  • matplotlib.axes
  • matplotlib.style
  • matplotlib.figure

Answer: matplotlib.pyplot. matplotlib.pyplot (conventionally imported as plt) is the high-level plotting interface.

What is the conventional import for pyplot?

  • import pyplot as plt
  • from matplotlib import plot as plt
  • import matplotlib.pyplot as plt
  • import matplotlib as plt

Answer: import matplotlib.pyplot as plt. The community convention is 'import matplotlib.pyplot as plt'.

Which function draws a line chart connecting x,y points?

  • plt.scatter(x, y)
  • plt.plot(x, y)
  • plt.bar(x, y)
  • plt.hist(x)

Answer: plt.plot(x, y). plt.plot draws a line connecting the points; scatter draws unconnected markers.

Which function is best for showing the distribution of a single dataset?

  • plt.bar
  • plt.plot
  • plt.scatter
  • plt.hist

Answer: plt.hist. plt.hist bins values and draws a histogram, ideal for visualizing how data is distributed.

What does fig, ax = plt.subplots() return?

  • A figure and an axes (or array of axes)
  • Two figures
  • An axes and a legend
  • Two axes only

Answer: A figure and an axes (or array of axes). plt.subplots() returns a Figure and one or more Axes objects, the basis of the object-oriented API.

In the object-oriented API, how do you set the title on an axes named ax?

  • plt.set_title('T')
  • ax.set_title('T')
  • ax.title('T')
  • fig.title('T')

Answer: ax.set_title('T'). On an Axes object you call ax.set_title(); the pyplot equivalent is plt.title().

Which call makes labels passed via the label= argument appear on the plot?

  • plt.show()
  • plt.grid()
  • plt.label()
  • plt.legend()

Answer: plt.legend(). plt.legend() (or ax.legend()) collects the label= values from plotted artists into a legend box.

How do you save a figure to a PNG file?

  • fig.download('chart.png')
  • plt.export('chart.png')
  • plt.savefig('chart.png')
  • plt.write('chart.png')

Answer: plt.savefig('chart.png'). plt.savefig('chart.png') (or fig.savefig) writes the current figure to disk; the format follows the extension.

What is the difference between the pyplot interface and the object-oriented API?

  • They produce different file formats
  • pyplot uses an implicit 'current' figure/axes state machine, while the OO API uses explicit fig/ax objects
  • pyplot is faster at rendering
  • Only the OO API can draw bars

Answer: pyplot uses an implicit 'current' figure/axes state machine, while the OO API uses explicit fig/ax objects. pyplot tracks an implicit current figure/axes; the OO API gives you explicit Figure and Axes objects, preferred for complex or multi-panel plots.

Which argument to plt.subplots creates a 2-by-2 grid of axes?

  • nrows=2, ncols=2
  • shape=4
  • grid=(2, 2)
  • rows=2, cols=2 only

Answer: nrows=2, ncols=2. plt.subplots(nrows=2, ncols=2) returns the figure and a 2x2 array of Axes you can index.

Continue this course