Skip to content

Matplotlib

Installation

# Basic installation
pip install matplotlib

# With optional dependencies
pip install matplotlib[complete]

# Check version
python -c "import matplotlib; print(matplotlib.__version__)"

# Check backend
python -c "import matplotlib; print(matplotlib.get_backend())"

Basic Setup

# Essential imports
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np

# Set backend (optional)
matplotlib.use('TkAgg')  # or 'Qt5Agg', 'Agg', etc.

# Enable inline plots in Jupyter
%matplotlib inline

# Basic figure creation
fig, ax = plt.subplots()
plt.show()

Core Functionality

Basic Plotting

# Line plot
x = np.linspace(0, 10, 100)
y = np.sin(x)

plt.figure(figsize=(8, 6))
plt.plot(x, y, label='sin(x)', linewidth=2, color='blue')
plt.xlabel('X-axis')
plt.ylabel('Y-axis')
plt.title('Simple Sine Wave')
plt.legend()
plt.grid(True)
plt.show()

# Multiple lines
plt.plot(x, np.sin(x), label='sin(x)')
plt.plot(x, np.cos(x), label='cos(x)', linestyle='--')
plt.legend()

Scatter Plot

# Basic scatter
x = np.random.randn(100)
y = np.random.randn(100)
colors = np.random.rand(100)
sizes = 1000 * np.random.rand(100)

plt.scatter(x, y, c=colors, s=sizes, alpha=0.6, cmap='viridis')
plt.colorbar()
plt.title('Scatter Plot with Color and Size')

Bar Charts

# Vertical bar chart
categories = ['A', 'B', 'C', 'D']
values = [23, 17, 35, 29]

plt.bar(categories, values, color=['red', 'green', 'blue', 'orange'])
plt.title('Bar Chart')
plt.ylabel('Values')

# Horizontal bar chart
plt.barh(categories, values)

# Grouped bar chart
x = np.arange(len(categories))
width = 0.35
plt.bar(x - width/2, values, width, label='Series 1')
plt.bar(x + width/2, [20, 25, 15, 30], width, label='Series 2')
plt.xticks(x, categories)
plt.legend()

Histograms

# Basic histogram
data = np.random.normal(100, 15, 1000)
plt.hist(data, bins=30, alpha=0.7, color='skyblue', edgecolor='black')
plt.title('Histogram')
plt.xlabel('Values')
plt.ylabel('Frequency')

# Multiple histograms
data1 = np.random.normal(100, 15, 1000)
data2 = np.random.normal(80, 20, 1000)
plt.hist([data1, data2], bins=30, alpha=0.7, label=['Dataset 1', 'Dataset 2'])
plt.legend()

Subplots and Figure Management

Creating Subplots

# Basic subplots
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(10, 8))

ax1.plot([1, 2, 3], [1, 4, 2])
ax1.set_title('Plot 1')

ax2.scatter([1, 2, 3], [1, 4, 2])
ax2.set_title('Plot 2')

ax3.bar([1, 2, 3], [1, 4, 2])
ax3.set_title('Plot 3')

ax4.hist(np.random.randn(100), bins=20)
ax4.set_title('Plot 4')

plt.tight_layout()  # Adjust spacing
plt.show()

# Subplot with different sizes
fig = plt.figure(figsize=(12, 8))
ax1 = plt.subplot(2, 2, 1)
ax2 = plt.subplot(2, 2, 2)
ax3 = plt.subplot(2, 1, 2)  # Spans two columns

GridSpec for Advanced Layouts

import matplotlib.gridspec as gridspec

fig = plt.figure(figsize=(10, 8))
gs = gridspec.GridSpec(3, 3)

ax1 = fig.add_subplot(gs[0, :])    # Top row, all columns
ax2 = fig.add_subplot(gs[1, :-1])  # Middle row, first two columns
ax3 = fig.add_subplot(gs[1:, -1])  # Right column, bottom two rows
ax4 = fig.add_subplot(gs[-1, 0])   # Bottom left
ax5 = fig.add_subplot(gs[-1, -2])  # Bottom center

Subplot with Shared Axes

# Shared x-axis
fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True, figsize=(8, 6))
ax1.plot(x, np.sin(x))
ax2.plot(x, np.cos(x))

# Shared y-axis
fig, (ax1, ax2) = plt.subplots(1, 2, sharey=True, figsize=(10, 4))

Customization

Colors and Styles

# Color specifications
plt.plot(x, y, color='red')           # Named color
plt.plot(x, y, color='#FF5733')       # Hex code
plt.plot(x, y, color=(0.1, 0.2, 0.5)) # RGB tuple
plt.plot(x, y, c='r')                 # Short form

# Line styles
plt.plot(x, y, linestyle='-')         # Solid
plt.plot(x, y, linestyle='--')        # Dashed
plt.plot(x, y, linestyle='-.')        # Dash-dot
plt.plot(x, y, linestyle=':')         # Dotted
plt.plot(x, y, ls=':')                # Short form

# Markers
plt.plot(x, y, marker='o')            # Circle
plt.plot(x, y, marker='s')            # Square
plt.plot(x, y, marker='^')            # Triangle up
plt.plot(x, y, marker='D')            # Diamond

# Combined format string
plt.plot(x, y, 'ro-')  # Red circles with solid line
plt.plot(x, y, 'g--^') # Green dashed line with triangle markers

Fonts and Text

# Font properties
plt.rcParams['font.family'] = 'serif'
plt.rcParams['font.size'] = 12

# Title and labels with custom fonts
plt.title('Title', fontsize=16, fontweight='bold')
plt.xlabel('X Label', fontsize=14, style='italic')
plt.ylabel('Y Label', fontsize=14, color='red')

# Text annotations
plt.text(0.5, 0.5, 'Sample Text', fontsize=12, 
         horizontalalignment='center', verticalalignment='center',
         transform=ax.transAxes)  # Relative coordinates

# Annotations with arrows
plt.annotate('Important Point', xy=(2, 1), xytext=(3, 4),
             arrowprops=dict(arrowstyle='->', color='red', lw=2))

Axis Customization

# Axis limits
plt.xlim(0, 10)
plt.ylim(-1, 1)
# or
ax.set_xlim(0, 10)
ax.set_ylim(-1, 1)

# Axis ticks
plt.xticks([0, 2, 4, 6, 8, 10])
plt.yticks([-1, -0.5, 0, 0.5, 1])

# Custom tick labels
plt.xticks([0, 1, 2, 3], ['A', 'B', 'C', 'D'])

# Tick formatting
from matplotlib.ticker import FuncFormatter
def currency(x, pos):
    return f'${x:.0f}'
ax.yaxis.set_major_formatter(FuncFormatter(currency))

# Logarithmic scale
plt.yscale('log')
plt.xscale('log')

# Grid customization
plt.grid(True, linestyle='--', alpha=0.7, color='gray')

Legends

# Basic legend
plt.plot(x, np.sin(x), label='sin(x)')
plt.plot(x, np.cos(x), label='cos(x)')
plt.legend()

# Legend positioning
plt.legend(loc='upper right')  # 'upper left', 'lower right', etc.
plt.legend(loc='best')         # Automatic best position
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')  # Outside plot

# Legend customization
plt.legend(frameon=True, fancybox=True, shadow=True, 
           ncol=2, fontsize=10, title='Legend Title')

Different Plot Types

Statistical Plots

# Box plot
data = [np.random.normal(0, std, 100) for std in range(1, 4)]
plt.boxplot(data, labels=['Group A', 'Group B', 'Group C'])

# Violin plot (requires seaborn or custom implementation)
# Error bars
x = [1, 2, 3, 4]
y = [1, 4, 2, 3]
yerr = [0.1, 0.2, 0.1, 0.3]
plt.errorbar(x, y, yerr=yerr, fmt='o-', capsize=5)

# Fill between
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
plt.fill_between(x, y1, y2, alpha=0.3, color='blue')

2D Plots

# Heatmap/Image
data = np.random.rand(10, 10)
plt.imshow(data, cmap='hot', interpolation='nearest')
plt.colorbar()

# Contour plot
x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) * np.cos(Y)

plt.contour(X, Y, Z, levels=10)
plt.contourf(X, Y, Z, levels=20, cmap='viridis')  # Filled contours
plt.colorbar()

# 3D plotting
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, Y, Z, cmap='viridis')

Pie Charts

# Basic pie chart
sizes = [25, 30, 15, 30]
labels = ['A', 'B', 'C', 'D']
colors = ['gold', 'yellowgreen', 'lightcoral', 'lightskyblue']
explode = (0, 0.1, 0, 0)  # Explode slice B

plt.pie(sizes, explode=explode, labels=labels, colors=colors,
        autopct='%1.1f%%', shadow=True, startangle=90)
plt.axis('equal')  # Equal aspect ratio

Saving Figures

# Save in different formats
plt.savefig('plot.png', dpi=300, bbox_inches='tight')
plt.savefig('plot.pdf', format='pdf')
plt.savefig('plot.svg', format='svg')
plt.savefig('plot.eps', format='eps')

# High-quality publication figure
plt.savefig('publication.png', dpi=300, bbox_inches='tight', 
            facecolor='white', edgecolor='none', transparent=False)

# Save with specific size
plt.figure(figsize=(10, 6))
# ... plotting code ...
plt.savefig('sized_plot.png', dpi=150)

Interactive Features

Event Handling

# Click event handling
def onclick(event):
    if event.inaxes is not None:
        print(f'Clicked at: ({event.xdata:.2f}, {event.ydata:.2f})')

fig, ax = plt.subplots()
ax.plot([1, 2, 3], [1, 4, 2])
fig.canvas.mpl_connect('button_press_event', onclick)
plt.show()

# Key press events
def onkey(event):
    print(f'Key pressed: {event.key}')

fig.canvas.mpl_connect('key_press_event', onkey)

Widgets (requires widget backend)

from matplotlib.widgets import Button, Slider

# Interactive slider
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)

# Initial plot
t = np.linspace(0, 10, 1000)
a0 = 1
f0 = 1
s = a0 * np.sin(2 * np.pi * f0 * t)
l, = plt.plot(t, s)

# Slider
ax_freq = plt.axes([0.25, 0.1, 0.5, 0.03])
slider = Slider(ax_freq, 'Frequency', 0.1, 5.0, valinit=f0)

def update(val):
    freq = slider.val
    l.set_ydata(a0 * np.sin(2 * np.pi * freq * t))
    fig.canvas.draw_idle()

slider.on_changed(update)
plt.show()

Advanced Features

Animations

from matplotlib.animation import FuncAnimation

# Animated sine wave
fig, ax = plt.subplots()
x = np.linspace(0, 2*np.pi, 100)
line, = ax.plot(x, np.sin(x))

def animate(frame):
    line.set_ydata(np.sin(x + frame/10))
    return line,

ani = FuncAnimation(fig, animate, frames=200, interval=50, blit=True)
plt.show()

# Save animation
ani.save('animation.gif', writer='pillow', fps=20)
ani.save('animation.mp4', writer='ffmpeg', fps=30)

Custom Colormaps

from matplotlib.colors import LinearSegmentedColormap

# Create custom colormap
colors = ['red', 'yellow', 'green', 'blue']
n_bins = 100
cmap = LinearSegmentedColormap.from_list('custom', colors, N=n_bins)

# Use custom colormap
data = np.random.rand(10, 10)
plt.imshow(data, cmap=cmap)
plt.colorbar()

Dual Axes

# Two different y-axes
fig, ax1 = plt.subplots()

x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.exp(x/5)

# First y-axis
ax1.plot(x, y1, 'g-')
ax1.set_xlabel('X data')
ax1.set_ylabel('sin(x)', color='g')
ax1.tick_params(axis='y', labelcolor='g')

# Second y-axis
ax2 = ax1.twinx()
ax2.plot(x, y2, 'b-')
ax2.set_ylabel('exp(x/5)', color='b')
ax2.tick_params(axis='y', labelcolor='b')

plt.show()

Customization & Best Practices

Style Sheets

# Available styles
print(plt.style.available)

# Use built-in styles
plt.style.use('seaborn-v0_8')
plt.style.use('ggplot')
plt.style.use('dark_background')

# Use multiple styles
plt.style.use(['seaborn-v0_8', 'seaborn-v0_8-darkgrid'])

# Context manager for temporary style
with plt.style.context('bmh'):
    plt.plot([1, 2, 3], [1, 4, 2])
    plt.show()

RC Parameters

# Global settings
plt.rcParams['figure.figsize'] = [10, 6]
plt.rcParams['font.size'] = 12
plt.rcParams['lines.linewidth'] = 2
plt.rcParams['grid.alpha'] = 0.3

# Context manager for temporary settings
with plt.rc_context({'font.size': 14, 'lines.linewidth': 3}):
    plt.plot([1, 2, 3], [1, 4, 2])

# Reset to defaults
plt.rcdefaults()

Performance Tips

# Use appropriate backends
import matplotlib
matplotlib.use('Agg')  # For server/batch processing

# Batch plotting
plt.ioff()  # Turn off interactive mode
for i in range(100):
    plt.figure()
    plt.plot(data[i])
    plt.savefig(f'plot_{i}.png')
    plt.close()  # Important: close figures to free memory

# Efficient line collection for many lines
from matplotlib.collections import LineCollection
lines = [np.column_stack([x, y + i]) for i in range(100)]
lc = LineCollection(lines, linewidths=0.5)
ax.add_collection(lc)

Integration with Other Libraries

NumPy Integration

# Matplotlib works seamlessly with NumPy arrays
x = np.linspace(0, 10, 100)
y = np.sin(x)
plt.plot(x, y)

# Masked arrays
y_masked = np.ma.masked_where(y < 0, y)
plt.plot(x, y_masked)

Pandas Integration

import pandas as pd

# DataFrame plotting
df = pd.DataFrame({
    'x': range(10),
    'y1': np.random.randn(10),
    'y2': np.random.randn(10)
})

# Direct pandas plotting
df.plot(x='x', y=['y1', 'y2'])

# Using matplotlib directly
plt.plot(df['x'], df['y1'], label='y1')
plt.plot(df['x'], df['y2'], label='y2')
plt.legend()

Common Gotchas & Best Practices

Memory Management

# Always close figures when done
fig, ax = plt.subplots()
# ... plotting code ...
plt.close(fig)  # or plt.close('all')

# Clear current figure
plt.clf()

# Clear current axes
plt.cla()

Backend Issues

# Check current backend
print(plt.get_backend())

# Set backend before importing pyplot
import matplotlib
matplotlib.use('TkAgg')  # Must be before importing pyplot
import matplotlib.pyplot as plt

Common Patterns

# Professional plotting setup
def setup_plot(figsize=(10, 6)):
    fig, ax = plt.subplots(figsize=figsize)
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)
    ax.grid(True, alpha=0.3)
    return fig, ax

# Context manager for consistent styling
from contextlib import contextmanager

@contextmanager
def plot_style(style='seaborn-v0_8', figsize=(10, 6)):
    with plt.style.context(style):
        fig, ax = plt.subplots(figsize=figsize)
        yield fig, ax
        plt.tight_layout()

# Usage
with plot_style() as (fig, ax):
    ax.plot([1, 2, 3], [1, 4, 2])
    ax.set_title('Styled Plot')
    plt.show()

Quick Reference

Essential Functions

Function Purpose Example
plt.figure() Create new figure plt.figure(figsize=(8,6))
plt.subplot() Add subplot plt.subplot(2,2,1)
plt.plot() Line plot plt.plot(x, y, 'r-')
plt.scatter() Scatter plot plt.scatter(x, y, c=colors)
plt.bar() Bar chart plt.bar(x, height)
plt.hist() Histogram plt.hist(data, bins=20)
plt.imshow() Display image/2D data plt.imshow(array, cmap='hot')
plt.savefig() Save figure plt.savefig('plot.png', dpi=300)

Format Strings

Code Meaning Code Meaning
- Solid line o Circle marker
-- Dashed line s Square marker
-. Dash-dot line ^ Triangle up marker
: Dotted line D Diamond marker
r Red g Green
b Blue k Black
c Cyan m Magenta
y Yellow w White

Color Maps

Common colormaps: viridis, plasma, inferno, magma, coolwarm, RdYlBu, seismic, hot, cool, spring, summer, autumn, winter, gray

Use: plt.imshow(data, cmap='viridis')