The Figure & Axes object model
Most beginners learn Matplotlib through plt.plot() and plt.title(). That works for one quick chart, but the moment you need two panels, a shared axis or a custom tick, the "stateful" pyplot style becomes confusing. The fix is to understand the object model that sits underneath every chart.
Three layers: Figure, Axes, Artist
| Object | What it is | Typical methods |
|---|---|---|
Figure |
The whole canvas / image file | fig.suptitle(), fig.savefig(), fig.colorbar() |
Axes |
One plotting area with its own x and y axis | ax.plot(), ax.bar(), ax.set_xlabel() |
Axis |
The x or y axis inside an Axes (ticks, limits) | ax.xaxis.set_major_formatter() |
Artist |
Anything drawn: lines, text, patches, legends | line.set_color(), text.set_fontsize() |
Note the naming trap: Axes (plural-looking) is one plot panel, while Axis is a single x or y ruler.
Pyplot style vs object-oriented style
import matplotlib.pyplot as plt
months = ["Apr", "May", "Jun", "Jul"]
sales = [4.2, 5.1, 4.8, 6.3] # lakh INR
# Implicit (pyplot) style: works on "the current axes"
plt.plot(months, sales)
plt.title("Monthly sales")
# Explicit (object-oriented) style: recommended
fig, ax = plt.subplots(figsize=(7, 4))
line, = ax.plot(months, sales, marker="o")
ax.set_title("Monthly sales - Jaipur store")
ax.set_ylabel("Sales (lakh INR)")
ax.set_ylim(0, 7)
line.set_linewidth(2.5) # every plot call returns Artists you can tweak
fig.tight_layout()
With the explicit style you always know which panel a command affects. This matters once you write functions such as def plot_city(ax, df): that draw into an Axes passed from outside - the function becomes reusable in any layout.
Setting many properties at once
ax.set() accepts several properties in one call, which keeps chart code compact:
ax.set(title="Footfall by weekday", xlabel="Day", ylabel="Visitors", xlim=(0, 6))
Adding Axes by hand
plt.subplots() builds a regular grid, but you can also place Axes anywhere with figure-relative coordinates (0 to 1). This is handy for an inset chart:
fig, ax = plt.subplots()
ax.plot(range(12), [3, 4, 4, 5, 7, 9, 8, 7, 6, 5, 4, 3])
inset = fig.add_axes([0.62, 0.6, 0.25, 0.25]) # left, bottom, width, height
inset.bar(["Online", "Store"], [35, 65])
inset.set_title("Channel %", fontsize=8)
For uneven grids use plt.subplot_mosaic("AAB;CCB"), which returns a dictionary of Axes keyed by letter - very readable for report layouts.
Getting back to objects
If you inherit pyplot code, plt.gcf() returns the current Figure and plt.gca() the current Axes, so you can migrate gradually. Every Axes also knows its parent: ax.figure.
Common mistakes
- Mixing styles: calling
plt.title()after creating several Axes titles only the last one. - Forgetting the comma in
line, = ax.plot(...)-plotreturns a list of lines. - Creating figures in a loop without
plt.close(fig), which slowly eats memory in scripts and Django views. - Confusing
fig.suptitle(whole figure) withax.set_title(one panel).
Practice
Write a function plot_branch(ax, name, values) that draws a line chart of 12 monthly values into the
Axes it receives and sets a title and y-label. Then create a 2x2 grid with plt.subplots and call the
function for four branches (Pune, Indore, Kochi, Lucknow) using random data. Add one fig.suptitle.