Data Preparation / Data Preprocessing Slides 📂 Introduction · 4 of 13 55 min read

Matplotlib in Python: The Complete Plotting Guide

Matplotlib is the foundational Python plotting library that Seaborn, pandas, and most other chart tools are built on. This guide covers the Figure/Axes model, the pyplot vs object-oriented APIs, and seven chart families — line, bar, scatter, histogram, heatmap, boxplot/violin, and subplots — plus styling, rcParams, and exporting publication-quality figures at 300 DPI.

Matplotlib in Python

The foundational plotting library every data scientist must master — from your first line chart to publication-quality, pixel-precise figures with full control.
Line & Bar Scatter & Hist Subplots & Heatmaps Publication Export

Press Next → or use ← → arrow keys

Section 01

Why Matplotlib? The Anatomy of a Figure

Matplotlib underpins almost every scientific visualisation tool in Python. Seaborn and Plotly are great for quick exploration — but when you need pixel-precise control (custom tick formatters, dual axes, shared-axis subplots), you always drop down to matplotlib.

Chart Title X axis label Y axis label series plt.figure() → Figure (canvas) fig.add_subplot() → Axes (plot area) ax.legend() ticks & spines
🧩
One Figure can hold many Axes

The Figure is the outer canvas; each Axes is a single plot with its own title, x/y labels, ticks, spines and legend. Understanding this hierarchy is the key to every advanced layout.

Section 01 · Setup

Global Setup & the Two Interfaces

Set your defaults once at the top of the notebook via rcParams — it signals professionalism and keeps every chart consistent.

import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import numpy as np

# Apply a global style sheet
plt.style.use('seaborn-v0_8-darkgrid')

plt.rcParams['figure.figsize']  = (10, 5)
plt.rcParams['figure.dpi']      = 120
plt.rcParams['axes.titlesize']  = 14
plt.rcParams['lines.linewidth'] = 2
Object-Oriented — the pro standard

fig, ax = plt.subplots() gives you explicit handles to every element. Recommended for anything real.

⚠️
Pyplot state machine — quick only

Stateful plt.plot() calls are fine for a throwaway chart, but cause confusing state bugs in multi-axes figures.

Section 02

Line Chart — Trends Over Time

Monthly Revenue vs Target Jan Jun Sep Dec ₹k Revenue Target
fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(months, revenue, color='#60a5fa', marker='o', label='Revenue')
ax.plot(months, target,  color='#f59e0b', linestyle='--', label='Target')
ax.fill_between(months, revenue, target, where=[r > t for r, t in zip(revenue, target)],
                alpha=0.15, color='#34d399', label='Above target')
ax.yaxis.set_major_formatter(mticker.FuncFormatter(lambda x, _: f'₹{x:.0f}k'))
Section 03

Bar Chart — Comparing Categories

Grouped — by Category & Gender Fashion Tech Home Male ₹18k ₹24k Horizontal — sorted revenue Home Fashion Beauty Tech
x = np.arange(len(categories)); width = 0.35
b1 = ax.bar(x - width/2, male_avg,   width, label='Male',   color='#60a5fa')
b2 = ax.bar(x + width/2, female_avg, width, label='Female', color='#f87171')
ax.bar_label(b1, fmt='₹%.0fk', padding=3)          # value labels on bars
ax.barh(np.array(cats)[np.argsort(rev)], np.sort(rev))  # sorted horizontal
Section 04

Scatter Plot — Up to Four Dimensions

Age vs Purchase — colour=rating, size=delivery days High-value outlier 5★ 1★ Rating Customer Age
sc = ax.scatter(df['age'], df['purchase'],
    c=df['rating'],           # colour = 3rd variable
    s=df['delivery_days'] * 8, # size = 4th variable
    cmap='viridis', alpha=0.6, edgecolors='white')
plt.colorbar(sc, ax=ax).set_label('Customer Rating')
ax.annotate('High-value outlier', xy=(58, 24800), xytext=(45, 23000),
    arrowprops=dict(arrowstyle='->', color='#f87171'))
Section 05

Histogram & KDE — Distribution Shape

Distribution of Purchase Amount Mean Median 3 income brackets
n, bins, patches = ax.hist(data, bins=35, density=True, color='#60a5fa', alpha=0.6)
kde = gaussian_kde(data)                       # overlay a smooth KDE curve
ax.plot(x_range, kde(x_range), color='#f59e0b', label='KDE')
for patch, edge in zip(patches, bins):        # colour bars by region
    if edge > data.mean(): patch.set_facecolor('#f59e0b')
ax.axvline(data.mean(), color='#f59e0b', linestyle='--')
Section 06

Subplots — Many Charts, One Figure

Sales Dashboard — Q4 2024 Revenue Trend Category Age Dist. Age vs Spend

plt.subplots(nrows, ncols) returns a grid of Axes for dashboards and side-by-side comparisons.

SUBPLOTS CHEATSHEET
gridsubplots(2, 2, figsize=(12,8))
sharesharex=True — linked zoom
mosaicsubplot_mosaic([['A','A'],['B','C']])
hideaxes[1,2].set_visible(False)
spacetight_layout(pad=2.0)
Section 07

Heatmap with imshow() — Correlations

Correlation Matrix 1.0 .62 -.31 .08 .62 1.0 .45 -.58 -.31 .45 1.0 .39 .08 -.58 .39 1.0 agespendratingdays

ax.imshow() renders any 2D array as a coloured grid — correlation matrices, pivot tables, confusion matrices.

im = ax.imshow(corr, cmap='coolwarm',
               vmin=-1, vmax=1)
plt.colorbar(im, ax=ax)
# annotate every cell
for i in range(n):
  for j in range(n):
    c = 'white' if abs(corr[i,j])>.6 else 'black'
    ax.text(j, i, f'{corr[i,j]:.2f}',
            ha='center', color=c)
🎨
Auto-contrast text

Switch annotation colour to white on saturated cells and black on pale ones so every value stays readable — a small touch that separates amateur from publication-ready heatmaps.

Section 08

Boxplot & Violin — Summary vs Full Shape

Boxplot — quartiles + notch Violin — full KDE density
🎻
Boxplot summarises · Violin reveals

A boxplot shows median, quartiles, whiskers and outliers (add notch=True for a 95% CI around the median). A violin plot overlays a KDE to expose the full shape — including bimodal humps a boxplot would hide.

Section 09

Styling & Saving Publication-Quality Figures

# swap the whole look in one line
plt.style.use('seaborn-v0_8-whitegrid')
plt.style.use('dark_background')
plt.style.use('ggplot')
plt.style.use('bmh')

# export at print quality
fig.savefig('chart.png', dpi=300,
            bbox_inches='tight')
fig.savefig('chart.svg')  # vector
fig.savefig('chart.pdf')  # vector
FormatBest for
PNG dpi=300Screen & web display
PDF / SVGPrint — vector, resizes with no quality loss
EPSLaTeX & legacy academic docs
darkgrid dark_background ggplot bmh
Pro · Dual Axes

Dual Axes — Two Scales on One Chart

When two series share an x-axis but live on wildly different scales — revenue in ₹ vs conversion rate in % — a single y-axis crushes one of them flat. ax.twinx() gives the second series its own right-hand axis.

Revenue (bars) vs Conversion % (line) 0 ₹50k ₹100k 0% 5% 10% Revenue Conversion
fig, ax = plt.subplots()
ax.bar(months, revenue, color='#60a5fa')      # left y-axis
ax2 = ax.twinx()                                # share x, new right y-axis
ax2.plot(months, conversion, color='#f87171', marker='o')
ax.set_ylabel('Revenue (₹)',  color='#60a5fa')
ax2.set_ylabel('Conversion %', color='#f87171')   # colour-match each axis!
Pro · Colour

Choosing the Right Colormap

A colormap is a data-encoding decision, not decoration. Prefer perceptually uniform maps where equal steps in data look like equal steps in colour — and stay colour-blind safe.

viridis ★ default plasma ✓ uniform cividis ✓ CVD-safe coolwarm ✓ diverging jet ⚠ avoid
Match map to data

Sequential (viridis, plasma) for low→high magnitudes. Diverging (coolwarm) when a midpoint like 0 matters. Qualitative (tab10) for unordered categories.

⚠️
Retire the rainbow

jet has bright bands that invent features that aren't in the data and vanish in greyscale or for colour-blind readers. Viridis fixes all three.

Pro · ML

Matplotlib in the ML Workflow

Beyond EDA, matplotlib is how you read your model — learning curves diagnose over/underfitting, and a confusion matrix shows exactly where a classifier fails.

Learning Curve gap = variance Training epochs train val
Confusion Matrix TP142 FN18 FP11 TN329 Predicted Actual
🧠
Read the shapes

A wide train–val gap means high variance (overfitting → regularise or get more data); both curves high and flat means high bias (underfitting). On the matrix, a bright off-diagonal cell is exactly the error class to fix.

Pro · Debug

Common Pitfalls & Their Fixes

SymptomThe trapThe fix
Blank image filesavefig() after show()Save before plt.show() — show() clears the figure
Cropped labelsRelying on defaultsplt.tight_layout() or bbox_inches='tight'
RAM climbs in a loopFigures never releasedplt.close(fig) after each save
Blurry exportScreen-DPI rasterdpi=300, or vector .svg/.pdf
Misleading colourscmap='jet'Perceptually uniform 'viridis'
Only last plot showsStateful plt. on many axesExplicit fig, ax = plt.subplots()
Dates overlapRaw datetime ticksfig.autofmt_xdate() + a date locator
Reference

The Everyday Cheat-Sheet

🖼️
Create
fig, ax = plt.subplots(figsize=(w,h))
plt.subplots(r, c, sharex=True)
ax.twinx()
✏️
Plot
ax.plot / bar / barh / scatter
ax.hist / boxplot / violinplot
ax.imshow / fill_between
🏷️
Label
ax.set_title / set_xlabel
ax.legend(framealpha=.3)
ax.annotate(...)
🎚️
Limits & ticks
ax.set_xlim / set_ylim
ax.set_xticks / set_xticklabels
ax.xaxis.set_major_formatter(...)
🎨
Style
plt.style.use('...')
plt.rcParams.update({...})
ax.spines[[...]].set_visible(False)
💾
Finish
plt.tight_layout()
fig.savefig('f.png', dpi=300)
plt.close(fig)
📌
The muscle-memory workflow

subplots → plot → label → style → tight_layout → save. Internalise that six-step loop and any figure, however complex, becomes routine.

Section 10 · Part 1

The Golden Rules — 1 to 4

📊 MATPLOTLIB BEST PRACTICE · RULES 1–4
1
Use the Object-Oriented interface. fig, ax = plt.subplots() — never rely on stateful plt.plot() beyond a throwaway chart. State machines cause silent bugs in multi-axes figures.
2
Always call plt.tight_layout(). Do it before every show or save — otherwise axis labels and titles routinely get cropped.
3
Remove chart junk. ax.spines[['top','right']].set_visible(False) — the default box adds weight without adding information.
4
Label axes with units. "Revenue (₹ thousands)" beats a bare "Revenue" — precision removes ambiguity for your reader.
Section 10 · Part 2

The Golden Rules — 5 to 7 & Takeaway

📊 MATPLOTLIB BEST PRACTICE · RULES 5–7
5
Export smart. dpi=300, bbox_inches='tight' for print; .svg/.pdf when the figure must resize without pixelation.
6
Annotate directly. Use ax.annotate() to label peaks and anomalies — a chart that needs a paragraph of caption to explain has already failed.
7
Set rcParams globally. Configure fonts, DPI and grids once at the top of the notebook instead of styling each chart by hand.
🎯
Matplotlib's verbosity is its superpower

Higher-level libraries fail the moment you hit a custom requirement — and the answer is always "drop down to matplotlib." Learn it deeply and you'll never be blocked by a chart requirement again.

📊 End of tutorial · Press to review, or click Restart