Three Ways of Drawing¶
Matplotlib behaves differently depending on where you run your code: Jupyter Notebook, Python files, or the terminal.
Mental Model
Matplotlib's display behavior depends on its backend. Jupyter auto-renders plots inline after each cell, Python scripts need an explicit plt.show() to open a window, and the interactive terminal updates on every command. The plotting code stays the same; only the "when does it appear on screen" part changes.
The architectural principle: Matplotlib separates drawing logic from rendering logic. Your code defines what to draw (data, encoding, layout). The backend decides when and how it appears (inline image, GUI window, file). This separation is why the same plotting code works across Jupyter, scripts, and terminals without modification.
Jupyter Notebook¶
In Jupyter notebooks, plots render automatically inline.
```python import matplotlib.pyplot as plt import numpy as np
x = np.linspace(0, 2*np.pi, 100) y = np.sin(x)
plt.plot(x, y)
No plt.show() needed - displays automatically¶
```
Key points:
- Neither
plt.show()norplt.draw()are required - Plots appear below the cell
- Use
%matplotlib inlinefor static images - Use
%matplotlib notebookfor interactive plots
Magic Commands¶
python
%matplotlib inline # Static images (default in modern Jupyter)
%matplotlib notebook # Interactive (zoom, pan)
%matplotlib widget # Interactive in JupyterLab
Python File (.py)¶
When running Python scripts, you must explicitly display plots.
```python
script.py¶
import matplotlib.pyplot as plt import numpy as np
x = np.linspace(0, 2*np.pi, 100) y = np.sin(x)
plt.plot(x, y) plt.show() # Required - opens interactive window ```
Key points:
plt.show()starts an event loop- Opens one or more interactive windows
- Script blocks until windows are closed
- Looks for all active figure objects
Running the Script¶
bash
python script.py
A window will open displaying the plot. The script continues after you close the window.
Terminal (Interactive Python)¶
In an interactive Python session, use plt.draw() for updates.
```python
import matplotlib.pyplot as plt import numpy as np
plt.ion() # Turn on interactive mode x = np.linspace(0, 10, 100) plt.plot(x, np.sin(x)) plt.draw() # Updates the figure ```
Key points:
plt.ion()enables interactive modeplt.draw()updates the figure without blockingplt.ioff()disables interactive mode- Useful for dynamic updates
Comparison¶
| Environment | Display Method | Blocking | Interactive |
|---|---|---|---|
| Jupyter | Automatic | No | Optional |
| Python file | plt.show() |
Yes | Yes |
| Terminal | plt.draw() |
No | Yes |
plt.show() vs plt.draw()¶
plt.show()¶
- Starts the event loop
- Blocks execution until window closed
- Displays all pending figures
- Used in scripts
python
plt.plot([1, 2, 3])
plt.show() # Blocks here
print("After show") # Runs after window closed
plt.draw()¶
- Redraws the current figure
- Non-blocking
- Used for animations and updates
- Requires interactive mode
python
plt.ion()
plt.plot([1, 2, 3])
plt.draw() # Non-blocking
print("Continues immediately")
Best Practices¶
- Jupyter: Let automatic display work; use magic commands for interactivity
- Scripts: Always end with
plt.show() - Interactive: Use
plt.ion()andplt.draw()for dynamic updates - Saving: Use
plt.savefig()beforeplt.show()in scripts
Key Takeaways¶
- Jupyter: automatic display, no
show()needed - Python files:
plt.show()required, blocks until closed - Terminal:
plt.draw()for non-blocking updates plt.ion()enables interactive mode- Choose the right approach for your environment
Runnable Example: matplotlib_cheatsheet.py¶
```python """ MATPLOTLIB QUICK REFERENCE CHEAT SHEET ======================================
This is a condensed reference for quick lookup while coding. For detailed explanations, see the full course files. """
============================================================================¶
IMPORTS¶
============================================================================¶
import matplotlib.pyplot as plt import numpy as np
============================================================================¶
TWO PLOTTING STYLES¶
============================================================================¶
MATLAB-Style (Simple, for quick plots)¶
if name == "main": plt.plot(x, y) plt.xlabel('X Label') plt.ylabel('Y Label') plt.title('Title') plt.show()
# OOP-Style (Recommended for complex plots)
fig, ax = plt.subplots()
ax.plot(x, y)
ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_title('Title')
plt.show()
# ============================================================================
# METHOD NAME DIFFERENCES
# ============================================================================
"""
MATLAB-style OOP-style
------------- ----------------
plt.xlabel() → ax.set_xlabel()
plt.ylabel() → ax.set_ylabel()
plt.title() → ax.set_title()
plt.xlim() → ax.set_xlim()
plt.ylim() → ax.set_ylim()
plt.xticks() → ax.set_xticks()
plt.yticks() → ax.set_yticks()
plt.legend() → ax.legend() # SAME
plt.grid() → ax.grid() # SAME
plt.plot() → ax.plot() # SAME
"""
# ============================================================================
# CREATING SUBPLOTS
# ============================================================================
# Single plot
fig, ax = plt.subplots()
# Multiple plots (2 rows, 3 columns)
fig, axes = plt.subplots(2, 3, figsize=(12, 8))
# axes.shape = (2, 3)
# Access: axes[row, col]
# With shared axes
fig, axes = plt.subplots(2, 1, sharex=True)
# Flatten for easy iteration
axes_flat = axes.flatten()
# ============================================================================
# PLOT() - LINE PLOTS
# ============================================================================
# Basic
ax.plot(x, y)
# With format string: '[marker][line][color]'
ax.plot(x, y, 'ro-') # Red circles with solid line
ax.plot(x, y, 'b--') # Blue dashed line
ax.plot(x, y, 'g:s') # Green dotted with squares
# With keyword arguments
ax.plot(x, y,
color='red', # or 'r' or '#FF0000'
linestyle='--', # or '--'
linewidth=2, # or lw
marker='o',
markersize=8, # or ms
markerfacecolor='blue', # or mfc
markeredgecolor='black', # or mec
alpha=0.7,
label='My Line')
# Multiple lines
ax.plot(x, y1, 'r-', label='Line 1')
ax.plot(x, y2, 'b--', label='Line 2')
ax.legend()
# ============================================================================
# HIST() - HISTOGRAMS
# ============================================================================
# Basic histogram
n, bins, patches = ax.hist(data, bins=30)
# With explicit bin edges (can be unequal!)
bins = [0, 1, 2, 5, 10]
n, bins, patches = ax.hist(data, bins=bins)
# Probability density (area = 1.0)
n, bins, patches = ax.hist(data, bins=30, density=True)
# Customization
ax.hist(data,
bins=30, # int or array
density=False, # False=counts, True=density
cumulative=False, # False or True
histtype='bar', # 'bar', 'step', 'stepfilled'
color='blue',
edgecolor='black',
alpha=0.7,
label='My Data')
# Return values
n # Array of counts or densities (length = num_bins)
bins # Array of bin edges (length = num_bins + 1)
patches # List of bar objects
# Key formula for density
# density[i] = count[i] / (N × bin_width[i])
# ============================================================================
# COLORS
# ============================================================================
# Short codes
'r' 'g' 'b' 'c' 'm' 'y' 'k' 'w'
# Full names
'red' 'green' 'blue' 'cyan' 'magenta' 'yellow' 'black' 'white'
# Hex codes
'#FF0000' # Red
'#00FF00' # Green
'#0000FF' # Blue
# RGB tuple (0-1)
(1.0, 0.0, 0.0) # Red
# RGBA tuple (0-1, with alpha)
(1.0, 0.0, 0.0, 0.5) # Semi-transparent red
# ============================================================================
# LINE STYLES
# ============================================================================
'-' # Solid
'--' # Dashed
':' # Dotted
'-.' # Dash-dot
'' # No line
# ============================================================================
# MARKERS
# ============================================================================
'.' # Point
'o' # Circle
's' # Square
'^' # Triangle up
'v' # Triangle down
'*' # Star
'+' # Plus
'x' # X
'D' # Diamond
# ============================================================================
# CUSTOMIZATION
# ============================================================================
# Labels and title
ax.set_xlabel('X Label', fontsize=12)
ax.set_ylabel('Y Label', fontsize=12)
ax.set_title('Title', fontsize=14, fontweight='bold')
# Limits
ax.set_xlim(0, 10)
ax.set_ylim(-1, 1)
# Ticks
ax.set_xticks([0, 2, 4, 6, 8, 10])
ax.set_xticklabels(['0', '2', '4', '6', '8', '10'])
# Grid
ax.grid(True)
ax.grid(True, alpha=0.3, linestyle='--')
# Legend
ax.legend()
ax.legend(loc='upper right', fontsize=12, framealpha=0.9)
# Spines (hide top and right)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# ============================================================================
# TEXT AND ANNOTATIONS
# ============================================================================
# Simple text
ax.text(5, 0.5, 'My text', fontsize=12)
# Text with box
ax.text(5, 0.5, 'Text', bbox=dict(boxstyle='round', facecolor='wheat'))
# Annotation with arrow
ax.annotate('Point of interest',
xy=(5, 0.5), # Point to annotate
xytext=(6, 0.8), # Text position
arrowprops=dict(arrowstyle='->'))
# ============================================================================
# FIGURE AND AXES
# ============================================================================
# Figure size
fig, ax = plt.subplots(figsize=(10, 6)) # Width, height in inches
# DPI (resolution)
fig, ax = plt.subplots(dpi=150)
# Tight layout (auto-adjust spacing)
plt.tight_layout()
# Save figure
plt.savefig('figure.png', dpi=300, bbox_inches='tight')
# ============================================================================
# GRIDSPEC (COMPLEX LAYOUTS)
# ============================================================================
import matplotlib.gridspec as gridspec
fig = plt.figure(figsize=(12, 8))
gs = gridspec.GridSpec(3, 3, figure=fig)
# Span multiple cells
ax1 = fig.add_subplot(gs[0, :]) # Row 0, all columns
ax2 = fig.add_subplot(gs[1:, 0]) # Rows 1-2, column 0
ax3 = fig.add_subplot(gs[1, 1:]) # Row 1, columns 1-2
# ============================================================================
# COMMON PATTERNS
# ============================================================================
# Single plot with customization
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(x, y, 'b-', linewidth=2, label='Data')
ax.set_xlabel('X', fontsize=12)
ax.set_ylabel('Y', fontsize=12)
ax.set_title('My Plot', fontsize=14, fontweight='bold')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# Histogram with density and PDF overlay
fig, ax = plt.subplots(figsize=(10, 6))
n, bins, patches = ax.hist(data, bins=30, density=True, alpha=0.7, label='Data')
ax.plot(x_pdf, pdf, 'r-', linewidth=3, label='Theory')
ax.set_xlabel('Value', fontsize=12)
ax.set_ylabel('Probability Density', fontsize=12)
ax.legend()
plt.show()
# Multiple subplots
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
for i, ax in enumerate(axes.flatten()):
ax.plot(x, y[i])
ax.set_title(f'Plot {i+1}')
plt.tight_layout()
plt.show()
# ============================================================================
# DEBUGGING CHECKLIST
# ============================================================================
"""
Common Mistakes:
1. Using plt.xlabel() instead of ax.set_xlabel() in OOP style
2. Forgetting that axes[row, col] (not axes[col, row])
3. Not unpacking hist() return values when needed
4. Confusing density=True (area=1) with density=False (counts)
5. Accessing axes[2, 1] when shape is (2, 2) (out of bounds)
6. Not calling plt.show() at the end
7. Forgetting that bins array has length = num_bins + 1
"""
# ============================================================================
# QUICK DIAGNOSIS
# ============================================================================
"""
Error: 'AxesSubplot' object has no attribute 'xlabel'
Fix: Use ax.set_xlabel() not ax.xlabel()
Error: IndexError: index 2 is out of bounds for axis 0 with size 2
Fix: Check axes.shape, remember axes[row, col]
Issue: Histogram looks weird
Check: bins parameter, try different values
Issue: Want to overlay PDF but scales don't match
Fix: Use density=True in hist()
Issue: Can't access individual subplots
Fix: Use axes.flatten() to convert to 1D array
"""
print("Cheat sheet loaded! Use as quick reference while coding.")
```
Why Behavior Differs: The Backend¶
The plotting code is always the same --- what changes is the backend (the rendering engine):
text
inline backend (Jupyter) → renders static PNG per cell
GUI backend (scripts) → opens interactive window, blocks at show()
interactive terminal → updates on every command
The backend controls when and how pixels reach the screen. This is why the same code behaves differently in Jupyter vs a .py script vs the REPL.
Exercises¶
Exercise 1. Write the same plot (a sine curve) using all three Matplotlib interfaces: (1) pyplot implicit, (2) pyplot explicit (fig, ax), and (3) pure OOP style.
Solution to Exercise 1
```python import matplotlib.pyplot as plt import numpy as np
np.random.seed(42)
Solution code depends on the specific exercise¶
x = np.linspace(0, 2 * np.pi, 100) fig, ax = plt.subplots() ax.plot(x, np.sin(x)) ax.set_title('Example Solution') plt.show() ```
See the content of this page for the relevant API details to construct the full solution.
Exercise 2. Explain the advantages and disadvantages of the pyplot interface versus the OOP interface. Which is recommended for production code?
Solution to Exercise 2
See the explanation in the main content of this page for the key concepts. The essential idea is to understand the API parameters and their effects on the resulting visualization.
Exercise 3. Write code using plt.subplots() (the recommended way) to create a 1x2 figure with different plots in each subplot.
Solution to Exercise 3
```python import matplotlib.pyplot as plt import numpy as np
np.random.seed(42) fig, axes = plt.subplots(1, 2, figsize=(12, 5))
x = np.linspace(0, 2 * np.pi, 100) axes[0].plot(x, np.sin(x)) axes[0].set_title('Left Subplot')
axes[1].plot(x, np.cos(x)) axes[1].set_title('Right Subplot')
plt.tight_layout() plt.show() ```
Adapt this pattern to the specific requirements of the exercise.
Exercise 4. Predict what happens if you mix pyplot calls (plt.plot()) and OOP calls (ax.plot()) in the same script. Can this cause confusion?
Solution to Exercise 4
```python import matplotlib.pyplot as plt import numpy as np
np.random.seed(42) x = np.linspace(0, 10, 100) fig, ax = plt.subplots() ax.plot(x, np.sin(x), 'b-', lw=2) ax.set_title('Solution') plt.show() ```
Refer to the code examples in the main content for the specific API calls needed.