Python with AI Tutorial · Chapter 40 of 48
Python Matplotlib is the standard library for charts. With a few lines you can draw line, bar and pie charts from lists or pandas DataFrames, label them, style them and save them as PNG files for reports and slides. This chapter builds each chart type step by step using the sales data from the pandas chapters.
Install Matplotlib and draw a line chart
Install with pip and import matplotlib.pyplot as plt. A line chart needs an x list and a y list; everything else is optional labelling.
pip install matplotlib
Output: Successfully installed matplotlib-3.9.2 ...
import matplotlib.pyplot as plt
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
revenue = [10469, 11273, 9850, 12980, 14120, 13560]
plt.plot(months, revenue, marker="o")
plt.title("Monthly Revenue 2026")
plt.xlabel("Month")
plt.ylabel("Revenue (USD)")
plt.grid(True)
plt.savefig("revenue_line.png", dpi=150, bbox_inches="tight")
plt.show()
Output: A window opens showing a line rising from 10,469 in Jan to a peak of 14,120 in May, with a dot on each month; the same chart is saved as revenue_line.png.
Call savefig() before show(). In a script, show() blocks until you close the window and then clears the figure, so a savefig() placed after it writes a blank image.
The Figure and Axes objects
The plt. functions are shortcuts. For anything beyond a quick look, create a figure and an axes with plt.subplots() and call methods on the axes. This object-oriented style is what you will see in most documentation.
regions = ["East", "North", "South", "West"]
totals = [3186, 5481, 5987, 7088]
fig, ax = plt.subplots(figsize=(7, 4))
bars = ax.bar(regions, totals, color="#2b6cb0")
ax.bar_label(bars, fmt="{:,.0f}")
ax.set_title("Revenue by Region")
ax.set_ylabel("Revenue (USD)")
fig.savefig("region_bar.png", dpi=150, bbox_inches="tight")
plt.show()
Output: Four blue bars labelled 3,186 5,481 5,987 7,088 above them, West the tallest.
bar_label() prints the value on each bar, and the fmt string adds thousands separators. figsize is in inches; combined with dpi=150 this gives a 1050 by 600 pixel image.
Chart types and when to use them
Pick the chart from the question you are answering, not from what looks impressive.
| Method | Chart | Best for |
|---|---|---|
ax.plot() |
Line | Trends over time (monthly revenue) |
ax.bar() / ax.barh() |
Vertical / horizontal bar | Comparing categories (revenue by region) |
ax.pie() |
Pie | Share of a whole, five slices or fewer |
ax.scatter() |
Scatter | Relationship between two numbers (units vs price) |
ax.hist() |
Histogram | Distribution of one number (order values) |
ax.boxplot() |
Box plot | Spread and outliers per group |
ax.stackplot() / fill_between() |
Area | Cumulative or stacked totals over time |
df.plot(kind=...) |
Any of the above | Quick charts straight from pandas |
Grouped bar chart
To compare two months per region, draw two bar series shifted left and right of each tick. NumPy supplies the positions.
import numpy as np
jan = [1998, 5481, 2990, 0]
feb = [1188, 0, 2997, 7088]
x = np.arange(len(regions))
width = 0.38
fig, ax = plt.subplots(figsize=(7, 4))
ax.bar(x - width / 2, jan, width, label="2026-01")
ax.bar(x + width / 2, feb, width, label="2026-02")
ax.set_xticks(x, regions)
ax.set_ylabel("Revenue (USD)")
ax.set_title("Revenue by Region and Month")
ax.legend()
plt.show()
Output: Pairs of bars per region in two colours with a legend; North has only a January bar and West only a February bar.
The width of 0.38 leaves a small gap between neighbouring pairs. Shifting each series by half the width keeps every pair centred on its tick, so the region names line up with the bars they describe.
Pie chart
Pie charts work for a handful of slices that add up to a whole. autopct prints the percentage on each slice.
products = ["Dashboard", "Tracker", "Calendar"]
share = [13986, 5083, 2673]
fig, ax = plt.subplots(figsize=(5, 5))
ax.pie(share, labels=products, autopct="%1.1f%%", startangle=90, counterclock=False)
ax.set_title("Revenue Share by Product")
plt.show()
Output: Three slices labelled Dashboard 64.3%, Tracker 23.4%, Calendar 12.3%, starting at the top and running clockwise.
If you have more than five categories, a horizontal bar chart sorted by value is almost always easier to read than a pie.
Paste your aggregated data and specify every visual detail you want.
I have this pandas Series from sales.groupby("Region")["Revenue"].sum():
Region
East 3186
North 5481
South 5987
West 7088
Write Matplotlib code (object-oriented style with fig, ax = plt.subplots) for a horizontal bar chart sorted largest at the top, bars in #2b6cb0 with the top bar highlighted in #dd6b20, value labels formatted like 7,088, no top or right spines, title "Revenue by Region, H1 2026", and save it as region_bars.png at 200 dpi with tight bounding box.
Plot straight from pandas
Every DataFrame and Series has a .plot() method that calls Matplotlib for you and returns the axes, so you can keep customising.
import pandas as pd
sales = pd.DataFrame({
"Region": ["North", "South", "East", "North", "West", "South", "East", "West"],
"Product": ["Dashboard", "Tracker", "Dashboard", "Calendar", "Tracker", "Dashboard", "Calendar", "Dashboard"],
"Units": [4, 10, 2, 15, 7, 3, 12, 5],
"Revenue": [3996, 2990, 1998, 1485, 2093, 2997, 1188, 4995],
})
ax = sales.groupby("Product")[["Units", "Revenue"]].sum().plot(
kind="bar", subplots=True, layout=(1, 2), figsize=(9, 4), legend=False, rot=0,
title=["Units by Product", "Revenue by Product"])
plt.tight_layout()
plt.show()
Output: Two bar charts side by side: Units (Calendar 27, Dashboard 14, Tracker 17) and Revenue (Calendar 2673, Dashboard 13986, Tracker 5083).
Everything pandas draws is still Matplotlib underneath, so plt.savefig() and the axes methods from the earlier sections apply unchanged to the returned axes.
Format the axes
Default tick labels such as 14000 look unfinished. A FuncFormatter rewrites each tick, and hiding the top and right spines gives a cleaner look.
from matplotlib.ticker import FuncFormatter
fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(months, revenue, marker="o", color="tab:green", linewidth=2)
ax.yaxis.set_major_formatter(FuncFormatter(lambda v, pos: f"${v / 1000:.0f}k"))
ax.spines[["top", "right"]].set_visible(False)
ax.set_title("Monthly Revenue 2026", loc="left", fontsize=14)
ax.annotate("Best month", xy=("May", 14120), xytext=("Mar", 14000),
arrowprops=dict(arrowstyle="->"))
plt.show()
Output: The y axis reads $10k, $11k ... $14k, the title sits at the left, and an arrow labelled "Best month" points at the May value.
Several charts in one figure
plt.subplots(rows, cols) returns a grid of axes. Fill each one, add a shared title and save a single dashboard image.
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(11, 4))
ax1.plot(months, revenue, marker="o", color="tab:green")
ax1.set_title("Monthly Revenue")
ax1.grid(axis="y", alpha=0.3)
ax2.bar(regions, totals, color="tab:orange")
ax2.set_title("Revenue by Region")
fig.suptitle("Sales Dashboard 2026", fontsize=15)
fig.tight_layout()
fig.savefig("dashboard.png", dpi=150)
print("Saved dashboard.png")
Output: Saved dashboard.png
plt.show(). In a plain script run from the terminal you need plt.show() to see a window, or savefig() to get a file. Call plt.close(fig) inside loops that create many figures to free memory.Describe a layout problem and ask for the fix with an explanation.
My Matplotlib bar chart has 12 product names on the x axis and the labels overlap. I am using fig, ax = plt.subplots(figsize=(8, 4)) and ax.bar(names, values). Show me three fixes: rotating labels 45 degrees with correct alignment, switching to ax.barh with the longest bar at the top, and wrapping long names onto two lines with textwrap. Also make the y axis show values as 1.2k instead of 1200.
Common mistakes
- Calling
savefig()aftershow()and getting an empty image. - Mixing the
plt.title()shortcuts with axes objects and wondering why labels land on the wrong subplot; stick toax.set_title()once you usesubplots(). - Plotting text categories in the wrong order. Sort the data first; Matplotlib draws in the order given.
- Using a pie chart for ten categories. Switch to a sorted bar chart.
- Forgetting
tight_layout()orbbox_inches="tight", which crops long axis labels in the saved file.
Exercise
Using the sales DataFrame above, draw a horizontal bar chart of total Units per Product, sorted so the largest bar is at the top, with the value printed at the end of each bar and the title “Units Sold by Product”. Save it as units_by_product.png.
Show answer
units = sales.groupby("Product")["Units"].sum().sort_values() # ascending puts largest on top in barh
fig, ax = plt.subplots(figsize=(7, 3.5))
bars = ax.barh(units.index, units.values, color="#2b6cb0")
ax.bar_label(bars, padding=3)
ax.set_title("Units Sold by Product")
ax.set_xlabel("Units")
fig.tight_layout()
fig.savefig("units_by_product.png", dpi=150)
print(units)
Output: Product Dashboard 14 Tracker 17 Calendar 27 Name: Units, dtype: int64
barh draws the first item at the bottom, so sorting ascending places Calendar (27) at the top of the chart.
Related chapters
- Python pandas Basics – the groupby results these charts visualise.
- Python NumPy –
np.arange()for bar positions. - Python in Excel – run Matplotlib inside an Excel cell.
- Python with AI course hub – all 48 chapters in order.
FAQ
What is Matplotlib used for?
Matplotlib draws static charts in Python: line, bar, pie, scatter, histogram and many more. It works with plain lists, NumPy arrays and pandas DataFrames and can save charts as PNG, SVG or PDF for reports, slides and websites.
How do I save a Matplotlib chart as an image?
Call fig.savefig(“chart.png”, dpi=150, bbox_inches=”tight”) or plt.savefig() before plt.show(). The file extension sets the format; use .svg or .pdf for vector output.
What is the difference between plt.plot and ax.plot?
plt.plot() draws on the current figure implicitly, which is fine for one quick chart. ax.plot() draws on a specific Axes object returned by plt.subplots(), giving you full control when you have several charts or detailed styling.
Working with spreadsheets too? Ready-made Excel, Google Sheets and Power BI templates are at NextGenTemplates.com.
Chapter 40 of 48 · Python with AI: all 48 chapters



