Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Data Analysis with Python (pandas, polars, DuckDB)  Module 10 – Visualising data with matplotlib

Plotting with matplotlib and pandas

Draw line, bar, scatter and histogram charts with matplotlib's Figure and Axes and with pandas .plot, then label, format and save them as SVG or PNG.

  • Beginner
  • 35 minutes
  • Examples run with Python 3.14.8, pandas 3.0.6 and matplotlib 3.11.2
  • By MySmartCoPilot
Free core, Pro depth: 13 of 35 minutes free
Free: Named Axes make the destination of every plotting call explicit · Four plotting methods create four inspectable kinds of marks · Labels and a chosen file format make a chart usable in a report. With a pass: 2 in-depth parts and 3 interview answers, about 22 minutes.

What you will learn

  • Build a Figure and named Axes with the object-oriented API
  • Choose line, bar, scatter and histogram methods for DataFrame columns
  • Label axes and distinguish tick formatting from bar-value formatting
  • Choose SVG or PNG and calculate a figure output size

Before you start

One shop needs a monthly revenue line and a category ranking in the same report. Start with a matplotlib Figure: The complete matplotlib image that owns one or more plotting areas and can be saved to a file., the whole image, and one or more matplotlib Axes: One matplotlib plotting area with its own data artists, title, scales, axis labels and ticks., its plotting areas. fig, ax = plt.subplots() creates both. Draw a line with ax.plot, bars with ax.bar or ax.barh, points with ax.scatter, and a histogram with ax.hist.

pandas draws from a DataFrame using matplotlib underneath. Pass ax=ax so it draws into your named Axes, then refine the result with titles, labels and formatters. fig.savefig("chart.svg") writes the Figure to a file. The examples use twelve monthly rows, six categories and 160 invented orders to show the objects each chart creates.

Named Axes make the destination of every plotting call explicit

A Figure holds Axes; each Axes holds a title, two Axis objects, the data artists and its legend; pandas .plot draws into it.Figure: the whole imagefig, ax = plt.subplots() · fig.savefig("chart.svg")Axes: one plotting area (fig.axes lists them)pandas: df.plot(ax=ax) draws into the same Axesand returns itTitle · ax.set_title()Two Axis objects: ax.xaxis and ax.yaxisticks, tick labels and a label · ax.set_xlabel()Artists that draw the dataax.plot → Line2D · ax.bar → Rectanglesax.scatter → PathCollection · ax.hist → RectanglesSpines, grid and legend · ax.legend()

What a matplotlib chart is made of

Text description of the diagram

The Figure is the whole image. plt.subplots() returns it together with one or more Axes, and fig.savefig writes it to a file.

Each Axes is one plotting area, and fig.axes lists them. An Axes holds:

  • its title, set with ax.set_title();
  • two Axis objects, ax.xaxis and ax.yaxis, each with its ticks, tick labels and label (ax.set_xlabel(), ax.set_ylabel());
  • the artists that draw the data: ax.plot adds a Line2D, ax.bar and ax.hist add Rectangles, ax.scatter adds a PathCollection;
  • its spines, grid and legend.

pandas draws into the same structure: df.plot(ax=ax) adds its artists to the Axes you pass and returns that Axes, so you can keep customising it with matplotlib.

matplotlib can be called in two styles. In the object-oriented style you create the Figure and the Axes and call their methods. In the pyplot style, functions such as plt.plot and plt.title draw on a “current” Figure and Axes that pyplot keeps for you. Both draw the same chart:

One chart in the object-oriented and the pyplot style Python · two_styles.py
import matplotlib.pyplot as plt
import pandas as pd

monthly = pd.read_csv("monthly.csv")

# Object-oriented style: make the Figure and the Axes, then call methods on the Axes.
fig, ax = plt.subplots()
ax.plot(monthly["month"], monthly["revenue"])
ax.set_title("Revenue by month")

# pyplot style: pyplot keeps a "current" Figure and Axes and draws on them.
plt.figure()
plt.plot(monthly["month"], monthly["revenue"])
plt.title("Revenue by month")
current = plt.gca()

for name, a in [("object-oriented", ax), ("pyplot", current)]:
    line = a.get_lines()[0]
    print(f"{name:>15}: {type(a.figure).__name__} with {len(a.figure.axes)} Axes, "
          f"{len(a.get_lines())} line of {len(line.get_xdata())} points, title {a.get_title()!r}")
same = list(ax.get_lines()[0].get_ydata()) == list(current.get_lines()[0].get_ydata())
print("same data in both:", same)
print("open figures:", plt.get_fignums())

Output

object-oriented: Figure with 1 Axes, 1 line of 12 points, title 'Revenue by month'
         pyplot: Figure with 1 Axes, 1 line of 12 points, title 'Revenue by month'
same data in both: True
open figures: [1, 2]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 two_styles.py

The matplotlib documentation suggests the object-oriented style for complicated charts and for code you will reuse, and pyplot for quick interactive looks. Plotting with matplotlib in a script or a report is the reusable kind, so this track writes fig, ax = plt.subplots() and calls Axes methods. Note the last line: pyplot keeps every figure it made, here two, until they are closed.

Follow the ownership chain used by the shop’s monthly chart.

Interactive · stage by stage Figure and named Axes on the lesson data Follow the ownership chain used by the shop's monthly chart.
Figure and named Axes on the lesson data, step 3 of 3. Showing Create a Figure, Name the Axes, Save the Figure. In focus: Save the Figure.Create a FigureWhole imageName the Axesax.plot; ax.set_titleSave the Figurefig.savefig

The drawing shows the whole scene; its 3 steps are listed below.

Every step as text
  1. Create a Figure. subplots creates the image and returns its plotting area.
  2. Name the Axes. Each method names the plotting area it changes.
  3. Save the Figure. Saving the owning Figure writes the complete image rather than one individual artist.

Naming the objects keeps a second chart from silently becoming the destination.

Key idea

A Figure owns the image; an Axes owns one plotting area’s artists, scales and labels.

Checkpoint

Check: Figure and named Axes

  1. Question 1 of 2 Which object represents the whole saved image?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: The Figure

    A Figure contains the plotting areas and is the object saved to a file.

  2. Question 2 of 2 two_styles.py creates one object-oriented chart and a separate pyplot chart. How many Figures does it leave open?

    Type a number.

    Show the answer: Question 2 of 2

    Answer: 2 figures

    Both construction paths register a Figure with pyplot, so the example leaves two before the process ends.

Four plotting methods create four inspectable kinds of marks

The shop’s sales live in two small tables, monthly.csv (orders and revenue for 12 months) and categories.csv (revenue per category for the year), plus 160 invented orders made from a fixed random seed. One figure holds a line, a bar chart, a scatter plot and a histogram. Two are drawn with Axes methods and two with pandas, into the Axes the figure already has:

A line, bars, a scatter and a histogram in one figure Python · four_kinds.py
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

plt.style.use("house.mplstyle")
monthly = pd.read_csv("monthly.csv")
categories = pd.read_csv("categories.csv")
rng = np.random.default_rng(42)                    # 160 invented orders, the same on every run
items = rng.integers(1, 9, size=160)
orders = pd.DataFrame({"items": items, "value": (items * rng.normal(420, 90, size=160)).round()})

fig, axs = plt.subplots(2, 2, figsize=(8.4, 6.0), layout="constrained")
line, bars, points, spread = axs.flat

# Axes methods: you pass the values.
line.plot(monthly["month"], monthly["revenue"] / 1e6)
line.set(title="Line: revenue by month", ylabel="Million", ylim=(0, 2.2))
line.set_xticks(monthly["month"][::3])

# pandas .plot: you pass column names and the Axes to draw on.
categories.sort_values("revenue").plot.barh(x="category", y="revenue", ax=bars, legend=False)
bars.set(title="Bar: revenue by category", xlabel="", ylabel="")
bars.xaxis.set_major_formatter(lambda v, pos: f"{v / 1e6:.0f}M")

orders.plot.scatter(x="items", y="value", ax=points, s=14, alpha=0.7, color="#1d5fcb")
points.set(title="Scatter: items and order value", xlabel="Items in the order", ylabel="Order value")

spread.hist(orders["value"], bins=20)
spread.set(title="Histogram: order values", xlabel="Order value")
fig.savefig("four-kinds.svg", metadata={"Date": None})

print("line:", len(line.get_lines()), "Line2D with", len(line.get_lines()[0].get_xdata()), "points")
print("bars:", len(bars.patches), "rectangles, longest at the top:", bars.get_yticklabels()[-1].get_text())
print("scatter:", len(points.collections[0].get_offsets()), "points in one collection")
print("histogram:", len(spread.patches), "bins, tallest holds", int(max(p.get_height() for p in spread.patches)), "orders")

Output

line: 1 Line2D with 12 points
bars: 6 rectangles, longest at the top: Books
scatter: 160 points in one collection
histogram: 20 bins, tallest holds 18 orders

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 four_kinds.py

Four charts of one shop: revenue by month as a line, by category as bars, order value against items, and a histogram.JanAprJulOct0.00.51.01.52.0MillionLine: revenue by month0M1M2M3M4MGardenKitchenToysElectronicsStationeryBooksBar: revenue by category2468Items in the order10002000300040005000Order valueScatter: items and order value20004000Order value051015Histogram: order values

The figure four_kinds.py draws

Text description of the diagram

A figure with four Axes in two rows, each drawn with a different method:

  • Top left, a line chart of revenue by month, in millions, on an axis from 0 to 2.2: it starts at 1.18 in January, dips to 1.09 in February, climbs to a peak of 1.93 in November and ends at 1.65 in December.
  • Top right, horizontal bars of revenue by category for the year, sorted with the largest at the top: Books, Stationery, Electronics, Toys, Kitchen and Garden, from about 4.4 million down to 1.2 million.
  • Bottom left, a scatter plot of 160 orders: the number of items, from 1 to 8, against the order value, whose median rises from 428 for one item to 3,249 for eight, with a wide spread at every count.
  • Bottom right, a histogram of the same 160 order values in 20 bins, spread from 235 to 4,798, with 85 % of the orders below 3,000 and a median of about 1,860.

Each chart type adds its own kind of object to its Axes, and the run counts them: one Line2D with 12 points, 6 bar rectangles, one collection of 160 points and 20 histogram rectangles. Knowing these objects is what lets a check, or a test, inspect a chart without looking at its pixels. Two smaller habits show in the code as well: ax.set(title=..., ylabel=...) sets several properties in one call, and layout="constrained" makes matplotlib leave room for every title and label, so the four charts do not overlap.

Axes methods or pandas .plot?
Criterion Axes methods (ax.plot, ax.bar …)pandas .plot (df.plot.bar …)
You pass Arrays or Series of valuesColumn names of the DataFrame
The x values Whatever you passThe index, unless you name x=
Labels and legend Only what you addColumn names as axis labels, and a legend
Returns The artists it drew (lines, bars)The Axes it drew on
Chart names plot, bar, barh, scatter, hist and moreline, bar, barh, hist, box, kde, area, pie, scatter, hexbin
When to choose Full control, or data that is not in a DataFrameA quick chart straight from a table, refined afterwards

pandas fills in some decisions for you, which is convenient until it is not:

What pandas .plot sets for you Python · pandas_defaults.py
import matplotlib.pyplot as plt
import pandas as pd

monthly = pd.read_csv("monthly.csv")

fig, ax = plt.subplots()
returned = monthly.plot(x="month", y=["orders"], ax=ax)
print("returns the Axes it drew on:", returned is ax)
print("x label from the column name:", repr(ax.get_xlabel()))
print("legend added:", ax.get_legend() is not None, [t.get_text() for t in ax.get_legend().get_texts()])

monthly.set_index("month")["revenue"].plot(ax=ax, secondary_y=False, legend=False)
print("lines after a second call:", [line.get_label() for line in ax.get_lines()])

fig2, ax2 = plt.subplots()
monthly.plot.bar(x="month", y="orders", ax=ax2, legend=False, rot=0)
print("bar chart x tick labels:", [t.get_text() for t in ax2.get_xticklabels()][:4], "...")

Output

returns the Axes it drew on: True
x label from the column name: 'month'
legend added: True ['orders']
lines after a second call: ['orders', 'revenue']
bar chart x tick labels: ['Jan', 'Feb', 'Mar', 'Apr'] ...

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 pandas_defaults.py

It returned the very Axes it was given, labelled the x-axis with the column name and added a legend for one line. A second call on the same Axes added a second line, which is how pandas charts are layered. Pass legend=False, or set the labels yourself, when the defaults do not fit the report.

Follow the tables into the four kinds of artists in four_kinds.py.

Interactive · stage by stage Methods and chart artists on the lesson data Follow the tables into the four kinds of artists in four_kinds.py.
Methods and chart artists on the lesson data, step 3 of 3. Showing Read the tables, Draw named marks, Inspect the artists. In focus: Inspect the artists.Read the tables12 months; 6 categoriesDraw named marksLine; bars; points; binsInspect the artistsOne Axes per chart

The drawing shows the whole scene; its 3 steps are listed below.

Every step as text
  1. Read the tables. Choose the monthly columns for the line and category values for the ranking.
  2. Draw named marks. The example uses a line, six bar rectangles, 160 paired points and twenty histogram rectangles.
  3. Inspect the artists. Read the corresponding artist collections instead of inferring their data from pixels.

The object counts describe this input and these calls; they are not universal counts for each chart kind.

Key idea

pandas can supply values from named columns; the resulting matplotlib artists remain available on the named Axes.

Checkpoint

Check: Methods and chart artists

  1. Question 1 of 2 What does passing ax=ax to pandas .plot specify?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: The destination plotting area

    The explicit Axes is used by the default matplotlib plotting backend.

  2. Question 2 of 2 four_kinds.py draws 160 orders in a scatter collection. How many paired points should that collection contain?

    Type a number.

    Show the answer: Question 2 of 2

    Answer: 160 points

    Each generated order contributes one items/value pair.

Labels and a chosen file format make a chart usable in a report

A report’s twelve monthly points need a title, units and a suitable output file. The reader should be able to read the chart without its code. Three Axes methods do most of that work: ax.set_title, ax.set_xlabel and ax.set_ylabel. For the numbers along an axis, matplotlib uses formatters. StrMethodFormatter formats every tick with one format string, such as "{x:,.0f}" for thousands separators. FuncFormatter turns each tick value into text with a function you write. ax.bar_label writes each bar’s value next to the bar.

fig.savefig writes the figure to a file and picks the format from the extension: .svg for vector graphics that stay sharp at any size, .png for a picture made of pixels. For a PNG, dpi sets how many pixels the file gets for each inch of figure size, and bbox_inches="tight" trims the empty margin around the chart. With these few calls, plotting with matplotlib covers the everyday charts of a report.

Follow the decisions that come after drawing the shop’s values.

Interactive · stage by stage Units and output files on the lesson data Follow the decisions that come after drawing the shop's values.
Units and output files on the lesson data, step 3 of 3. Showing Name the measure, Format the values, Choose the file. In focus: Choose the file.Name the measureRevenue and its unitFormat the valuesTicks and bar labelsChoose the fileSVG or PNG

The drawing shows the whole scene; its 3 steps are listed below.

Every step as text
  1. Name the measure. A title and axis labels explain what the twelve monthly points mean.
  2. Format the values. Formatters change tick text; bar_label changes text beside individual bars.
  3. Choose the file. Save the Figure in the format required by the report destination.

A file extension chooses an output format; it does not repair missing units or ambiguous labels.

Key idea

Keep the unit explicit and choose vector or raster output for the destination.

Checkpoint

Check: Units and output files

  1. Question 1 of 2 What does dpi control for an ordinary PNG export?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Pixels per inch of the Figure size

    The Figure's inch dimensions and dpi set its raster dimensions before tight-bounds cropping.

  2. Question 2 of 2 A slide needs a raster image, while a web chart must remain sharp when enlarged. Which formats fit?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: PNG for the slide; SVG for scalable vector output

    PNG is a pixel image and ordinary SVG chart paths are vector marks.

Tick labels and value labels that read like a report Pro: opens with a Pro, Premium or Ultimate pass

A reader should never have to count zeros or decode 1e6. This part compares tick formatters on one axis, fixes value labels that come out in scientific notation, and builds the Indian digit grouping that Python’s format strings cannot produce.

In this part:

  • One chosen unit must govern ticks and bar-value labels
  • Digit grouping and the rupee prefix are separate operations

3 diagrams · 4 runnable examples (Python) · about 6 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Saving figures at a fixed size, with identical files on every run Pro: opens with a Pro, Premium or Ultimate pass

A chart that goes into a report, a slide deck and a repository should come out the same size and, where possible, the same bytes every time. This part measures what figure size, dpi and the file format change, makes repeated SVG saves identical, and keeps the number of open figures at zero when a loop draws hundreds of charts.

In this part:

  • Fixed inches and dpi determine pixels until cropping changes the bounds
  • Dates and salted identifiers can change an unchanged SVG
  • Saving a pyplot Figure does not remove its registry reference
  • Your turn: the monthly report in one Figure

3 diagrams · 5 runnable examples (Python) · about 8 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Practice

Test your Figure and Axes choices on a shop report: select the destination, put the largest horizontal bar at the top and calculate an export size.

Practice that adapts to you

Practise this lesson

6 questions about 8 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 6 fig, ax = plt.subplots(), then result = monthly.plot(x="month", y="orders", ax=ax). What is result?

    Choose one answer.

    Show the answer: Question 1 of 6

    Answer: The same Axes object as ax, now holding the line pandas drew

    pandas draws with matplotlib and returns the Axes it drew on. With ax=ax that is your own Axes, so every Axes method still works on it afterwards: titles, labels, tick formatters, value labels.

  2. Question 2 of 6 revenue is a Series with one value per category. Which call draws horizontal bars with the largest at the top?

    Choose one answer.

    Show the answer: Question 2 of 6

    Answer: revenue.sort_values().plot.barh()

    barh draws the first value at the bottom and goes up, so sorting from the smallest to the largest puts the largest bar at the top. Sorting in descending order puts it at the bottom, and plot.bar draws vertical bars.

  3. Question 3 of 6 A figure is 6 inches wide. How many pixels wide is the PNG that fig.savefig("chart.png", dpi=200) writes?

    Type a number.

    Show the answer: Question 3 of 6

    Answer: 1200 pixels

    dpi is the number of pixels per inch of figure size, so 6 inches at 200 dots per inch is 1,200 pixels. The same figure saved as SVG keeps its size in points instead, 72 per inch, and scales without losing sharpness.

  4. Question 4 of 6 For another report: which object represents the whole saved image?

    Choose one answer.

    Show the answer: Question 4 of 6

    Answer: The Figure

    A Figure contains the plotting areas and is the object saved to a file.

  5. Question 5 of 6 For another report: what does passing ax=ax to pandas .plot specify?

    Choose one answer.

    Show the answer: Question 5 of 6

    Answer: The destination plotting area

    The explicit Axes is used by the default matplotlib plotting backend.

  6. Question 6 of 6 For another report: what does dpi control for an ordinary PNG export?

    Choose one answer.

    Show the answer: Question 6 of 6

    Answer: Pixels per inch of the Figure size

    The Figure's inch dimensions and dpi set its raster dimensions before tight-bounds cropping.

Interview questions

  1. Fresher · Warm-up

    What is the difference between a Figure and an Axes in matplotlib, and why do many analysts start every chart with fig, ax = plt.subplots()?

    A Figure is the whole image: the canvas that is saved to a file, with its size and background. An Axes is one plotting area inside it, with its own title, x- and y-axis, ticks, legend and the objects that draw the data, so one Figure can hold several Axes side by side. plt.subplots() creates the Figure and its Axes in one call and returns both. Every later call then names the object it changes, ax.set_title or fig.savefig, instead of relying on whatever pyplot considers current, which keeps code with several charts unambiguous and easy to reuse in functions. pandas fits in too, because df.plot(ax=ax) draws into the Axes you pass and returns it. Do not confuse Axes with Axis: an Axes has two Axis objects, ax.xaxis and ax.yaxis.

  2. Fresher to mid

    The value labels on a revenue bar chart read 4.41e+06, 3.72e+06 and so on, and the y-axis runs from 0.5 to 2.0 with a small 1e6 above it. Explain both effects and fix the chart so that nobody has to count zeros.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Mid to senior

    A nightly job saves 40 charts as SVG files into a Git repository. Every night all 40 files show up as changed, even on nights when the data did not change. What is going on, and how do you make unchanged charts produce unchanged files?

    Model answer, Pro. First step and follow-ups

  4. Mid to senior

    A script draws one chart per store for 3,000 stores. It gets slower and slower and is killed for using too much memory after a few hundred charts. What is happening, and how do you restructure it?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • fig, ax = plt.subplots() gives you the Figure and its Axes; draw with ax.plot, ax.bar, ax.barh, ax.scatter and ax.hist.
  • pandas df.plot(..., ax=ax) draws into your Axes and returns it, so pandas charts can be refined with matplotlib.
  • Each chart type adds its own objects to the Axes, which is what checks and tests can inspect.
  • Label every axis, format the ticks for the reader, and save with fig.savefig, choosing SVG or PNG by where the chart will be shown.

Terms in this lesson

matplotlib Axes
One matplotlib plotting area with its own data artists, title, scales, axis labels and ticks.
matplotlib Figure
The complete matplotlib image that owns one or more plotting areas and can be saved to a file.

Corrections

  • Added object and export scenes, checkpoints and adaptive practice; corrected the Indian-grouping description while retaining all chart programs and the Figure worksheet.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.