Python Matplotlib Tutorial

Create clear charts with Matplotlib's Figure and Axes, label them properly, combine several plots and save them as image files.

Lesson 77 of 107Advanced12 min read

Matplotlib is Python's foundational plotting library. It draws line charts, bar charts, scatter plots, histograms and much more, and saves them as PNG, SVG or PDF files. pandas and seaborn use it under the hood.

Python
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4], [8, 11, 9, 14], marker="o")
ax.set(xlabel="Session", ylabel="Attendees", title="Sailing school attendance")
fig.savefig("attendance.png", dpi=150)

Install it with python -m pip install matplotlib. This tutorial uses the object-oriented style, where you work with explicit Figure and Axes objects, because it scales cleanly from one chart to dashboards of several. It covers the main plot types, labels and scales, subplots and saving files. The examples chart attendance at a sailing school. Choosing the right chart for your data is covered in data visualization.

Figure and axes

A Matplotlib chart has two main objects:

  • The Figure is the whole image: its size, background and everything on it.
  • An Axes is one plotting area inside the figure, with its own x and y axis, title, data and legend. A figure can contain several.
Python
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(6, 4))          # one figure, one axes, 6x4 inches
fig, (left, right) = plt.subplots(1, 2, figsize=(10, 4))   # two side by side

plt.subplots() returns both, and you then call methods on the axes: ax.plot(), ax.bar(), ax.set_title(). The pyplot module also has functions like plt.plot() that act on a "current" axes; they're fine for quick experiments, but explicit axes avoid confusion once there's more than one plot.

"Axes" here means a plotting area, not the axis= argument from NumPy reductions, and not just the x and y lines, which Matplotlib calls Axis objects.

Plotting data

Each plot method draws a different kind of mark from your data:

MethodChartUse for
ax.plot(x, y)linetrends over an ordered sequence such as time
ax.bar(categories, values)barcomparing amounts between categories
ax.barh(...)horizontal barmany or long category names
ax.scatter(x, y)scatterrelationship between two measurements
ax.hist(values, bins=10)histogramdistribution of one measurement
Python
sessions = [1, 2, 3, 4, 5]
attendees = [8, 11, 9, 14, 12]
ax.plot(sessions, attendees, marker="o", label="Dinghy course")
ax.bar(["Mon", "Wed", "Sat"], [6, 9, 15], color="tab:blue")

x and y must have the same length, because each point pairs one x with one y; otherwise Matplotlib raises ValueError: x and y must have same first dimension. Calling a plot method several times on the same axes overlays the series, and label= names each one for the legend. Plot methods accept lists, NumPy arrays and pandas Series alike.

Labels and scales

A chart without labels makes the reader guess. Every chart that leaves your computer should have:

  • a title saying what it shows;
  • axis labels with units: "Attendees (people)", "Wind speed (knots)";
  • a legend when there's more than one series (ax.legend()).
Python
ax.set(title="Weekly attendance, spring term",
       xlabel="Week", ylabel="Attendees (people)")
ax.set_ylim(0, None)          # start the y axis at zero
ax.legend()
ax.grid(alpha=0.3)

Scales can mislead. A bar chart whose y axis starts at 80 instead of 0 makes a change from 85 to 90 look enormous; bar lengths should start at zero. Line charts may zoom in when the variation itself is the point, but say so in the labels. A logarithmic scale (ax.set_yscale("log")) suits data spanning several orders of magnitude, and must be labelled clearly. fig.tight_layout() stops labels from overlapping or being cut off.

Saving artifacts

fig.savefig() writes the figure to a file; the extension chooses the format:

Python
fig.savefig("attendance.png", dpi=150, bbox_inches="tight")   # raster image
fig.savefig("attendance.svg")                                 # scalable vector
plt.close(fig)                                                # free the memory
  • PNG suits web pages and documents; increase dpi for sharper images.
  • SVG and PDF are vector formats that stay sharp at any size, ideal for print and slides.
  • bbox_inches="tight" trims excess white space around the chart.

Call savefig() before plt.show(), because closing the window can leave an empty figure behind. Scripts that only save files don't need a window at all: on servers and in scheduled jobs Matplotlib automatically uses a non-interactive backend, and you can force one with matplotlib.use("Agg"). Close figures you've finished with, especially in loops that create many charts, or memory use grows with every figure.

Independent practice and a worked check

Task: export an attendance figure with units, readable labels and an intentional scale.

Python
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

months = ["Apr", "May", "Jun", "Jul"]
students = [42, 55, 61, 58]

fig, ax = plt.subplots(figsize=(6, 4))
ax.bar(months, students, color="tab:blue")
ax.set(title="Sailing school students per month, 2026",
       xlabel="Month", ylabel="Students (people)")
ax.set_ylim(0, 70)                         # bars start at zero
for x, value in zip(months, students):
    ax.annotate(str(value), (x, value), ha="center", va="bottom")
fig.tight_layout()
fig.savefig("students.png", dpi=150)
plt.close(fig)

The y axis starts at zero so bar lengths are proportional, each bar is labelled with its value, and the title states what and when. For the worked check, the object that represents an individual plotting area is the Axes.

Reference and next practice

The Matplotlib quick start guide and plot types gallery show what's possible. Continue with data visualization and charts and data cleaning. Practise with the Python exercises and Python quiz.

Matplotlib Tutorial Summary

  • Figure and axes: A Figure holds the overall graphic while Axes hold plotting areas and their scales.
  • Plotting data: Plot calls map supplied x and y values into graphical marks.
  • Labels and scales: Titles, axis labels and appropriate scales help a reader interpret the measurements.
  • Saving artifacts: savefig exports a graphic that can be shared independently of an interactive window.

Matplotlib Tutorial Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Matplotlib Tutorial in a small program

This saves sailing_attendance.png in the current folder and prints nothing; open the file to see the chart.

Python
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4], [8, 11, 9, 14], marker="o")
ax.set(xlabel="Session", ylabel="Attendees", title="Sailing school attendance")
fig.savefig("sailing_attendance.png", dpi=150)
plt.close(fig)
Line-by-line explanation
  1. Line 2One figure containing one axes.
  2. Line 3A line through four points, with a marker on each.
  3. Line 4set() applies the axis labels and title in one call.
  4. Lines 5 to 6The figure is saved as a 150-dpi PNG and then closed to free memory.
Example 2

Two charts in one figure, checked in code

Python
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from pathlib import Path

weeks = [1, 2, 3, 4, 5, 6]
dinghy = [8, 11, 9, 14, 12, 15]
keelboat = [5, 6, 6, 7, 9, 8]
days = ["Mon", "Wed", "Sat"]
bookings = [6, 9, 15]

fig, (trend, by_day) = plt.subplots(1, 2, figsize=(10, 4))
trend.plot(weeks, dinghy, marker="o", label="Dinghy")
trend.plot(weeks, keelboat, marker="s", label="Keelboat")
trend.set(title="Weekly attendance", xlabel="Week", ylabel="Attendees (people)")
trend.set_ylim(0, None)
trend.legend()
by_day.bar(days, bookings, color="tab:green")
by_day.set(title="Bookings by day", ylabel="Bookings")
fig.tight_layout()

out = Path("attendance_report.png")
fig.savefig(out, dpi=120)
plt.close(fig)
print(len(fig.axes), "axes;", len(trend.lines), "lines;", len(by_day.patches), "bars")
print(trend.get_legend_handles_labels()[1])
print(out.exists(), out.stat().st_size > 0)
out.unlink()
Output
2 axes; 2 lines; 3 bars
['Dinghy', 'Keelboat']
True True
Line-by-line explanation
  1. Line 2The non-interactive Agg backend renders to files without opening a window.
  2. Line 12Two axes side by side, unpacked into descriptive names.
  3. Line 16Starting the y axis at zero keeps the visual comparison honest.
  4. Lines 26 to 28Inspecting the figure objects confirms the chart contains what you intended, which is useful in automated tests.

Common Matplotlib Tutorial Mistakes

A chart with no labels or units

Don't
fig, ax = plt.subplots()
ax.plot(weeks, dinghy)
fig.savefig("chart.png")          # what is plotted? in what units?
Do
fig, ax = plt.subplots()
ax.plot(weeks, dinghy, label="Dinghy")
ax.set(title="Weekly attendance", xlabel="Week", ylabel="Attendees (people)")
ax.legend()
fig.savefig("chart.png")

Calling show() before savefig()

Don't
plt.plot(weeks, dinghy)
plt.show()
plt.savefig("chart.png")          # may save an empty image
Do
fig, ax = plt.subplots()
ax.plot(weeks, dinghy)
fig.savefig("chart.png")
plt.show()

Matplotlib Tutorial in Real Projects

Matplotlib in practice

Scientists use Matplotlib for publication figures, analysts for charts in reports and notebooks, and engineers for monitoring plots generated by scheduled scripts. pandas exposes it through df.plot(), and seaborn builds statistical charts on top of it with better defaults. Automated reports often loop over regions or products, creating one figure each, saving it and closing it.

For interactive web dashboards, libraries such as Plotly, Bokeh and Altair are common, but Matplotlib remains the standard for static images and the library most other Python plotting tools interoperate with.

Matplotlib Tutorial FAQ

What is the difference between plt.plot and ax.plot?

plt.plot() draws on Matplotlib's "current" axes, convenient for quick single plots. ax.plot() draws on a specific axes object you created, which is clearer and necessary when a figure has several plots.

How do I save a Matplotlib plot as an image?

Call fig.savefig("chart.png", dpi=150, bbox_inches="tight") before showing or closing the figure. Use .svg or .pdf for vector output that stays sharp at any size.

How do I make several plots in one figure?

fig, axes = plt.subplots(rows, cols) returns a figure and an array of axes. Draw on each one, for example axes[0, 1].bar(...), and call fig.tight_layout() to space them.

Why is my Matplotlib figure blank when saved?

Usually because savefig() was called after plt.show() or plt.close(), or on a different figure from the one you drew on. Keep a reference with fig, ax = plt.subplots() and call fig.savefig() before showing.

Should I use Matplotlib or seaborn?

seaborn is built on Matplotlib and makes statistical charts, such as grouped bars and distributions, quicker to create with attractive defaults. Learn Matplotlib's Figure and Axes first, because you'll use them to customise seaborn charts too.

Matplotlib Tutorial Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Multiple choiceeasy

Which object represents an individual plotting area?

Multiple choicemedium

Which review concern is valid for matplotlib tutorial?

Matplotlib Tutorial Quiz

2 questions to check what you learned.

  1. Which object represents an individual plotting area?

    • The filename
    • Axes
    • Python ignores the distinction
  2. Which statement correctly describes labels and scales?

    • Titles, axis labels and appropriate scales help a reader interpret the measurements.
    • Only the example output matters, not the rule
    • Every operation has the same behavior