matplotlib
Use when creating static, animated, or interactive visualizations with Matplotlib; Use when plotting data, customizing figures, or building publication-quality charts in Python
Files1
View on GitHubSKILL.mdskills/matplotlib/SKILL.md
GitHubYou are an expert in Matplotlib, Python's foundational plotting library for creating static, animated, and interactive visualizations.
Use this skill when
- Creating line, bar, scatter, histogram, heatmap, pie, or 3D plots
- Customizing figure aesthetics (colors, fonts, styles, annotations)
- Building multi-panel / subplot layouts
- Generating publication-quality or export-ready figures
- Animating plots with
FuncAnimation - Integrating Matplotlib with numpy, pandas, or Polars data
Do not use this skill when
- The user needs interactive browser-based charts (use Plotly or Bokeh instead)
- The task is purely data wrangling with no visualization
Instructions
- Always use the object-oriented API (
fig, ax = plt.subplots()), not the state-machineplt.*API. - Label axes, add titles, and use legends for clarity.
- Use
plt.tight_layout()orconstrained_layout=Trueto avoid clipping. - Save with
fig.savefig()at an appropriate DPI beforeplt.show().
Core API
Figure & Axes Setup
import matplotlib.pyplot as plt
import numpy as np
# Single plot
fig, ax = plt.subplots(figsize=(8, 5))
# Multiple subplots
fig, axes = plt.subplots(2, 3, figsize=(12, 7))
# Constrained layout (better than tight_layout)
fig, ax = plt.subplots(figsize=(8, 5), layout="constrained")
# Unequal subplot sizes with gridspec
from matplotlib.gridspec import GridSpec
gs = GridSpec(2, 2, figure=fig)
ax1 = fig.add_subplot(gs[0, :]) # span full row
ax2 = fig.add_subplot(gs[1, 0])
ax3 = fig.add_subplot(gs[1, 1])
Common Plot Types
x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(x)
# Line plot
ax.plot(x, y, color="steelblue", linewidth=2, linestyle="--", label="sin(x)")
# Scatter plot
ax.scatter(x, y, s=20, c="tomato", alpha=0.7, label="points")
# Bar chart
categories = ["A", "B", "C"]
values = [3, 7, 5]
ax.bar(categories, values, color="teal", edgecolor="black")
# Horizontal bar
ax.barh(categories, values, color="teal")
# Histogram
data = np.random.randn(1000)
ax.hist(data, bins=30, color="slateblue", edgecolor="white", alpha=0.8)
# Box plot
ax.boxplot([np.random.randn(100), np.random.randn(100)], labels=["A", "B"])
# Heatmap / imshow
matrix = np.random.rand(10, 10)
im = ax.imshow(matrix, cmap="viridis", aspect="auto")
fig.colorbar(im, ax=ax, label="Value")
# Pie chart
ax.pie(values, labels=categories, autopct="%1.1f%%", startangle=90)
# Stem plot
ax.stem(x[:20], y[:20])
# Step plot
ax.step(x, y, where="mid")
# Fill between
ax.fill_between(x, y - 0.2, y + 0.2, alpha=0.3, color="steelblue")
# Error bars
ax.errorbar(x[::10], y[::10], yerr=0.1, fmt="o", capsize=4)
Axes Customization
ax.set_title("My Chart", fontsize=14, fontweight="bold", pad=12)
ax.set_xlabel("X Axis", fontsize=12)
ax.set_ylabel("Y Axis", fontsize=12)
ax.set_xlim(0, 10)
ax.set_ylim(-1.5, 1.5)
ax.set_xticks([0, np.pi, 2 * np.pi])
ax.set_xticklabels(["0", "π", "2π"])
ax.tick_params(axis="both", labelsize=10)
ax.xaxis.set_tick_params(rotation=45)
ax.legend(loc="upper right", fontsize=10, framealpha=0.8)
ax.grid(True, linestyle="--", alpha=0.5)
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
Annotations & Text
ax.annotate(
"Peak",
xy=(np.pi / 2, 1), # point to annotate
xytext=(np.pi / 2 + 0.5, 0.7), # label position
arrowprops=dict(arrowstyle="->", color="black"),
fontsize=10,
)
ax.text(1, 0.5, "Note", transform=ax.transAxes, ha="right") # axes-relative
ax.axhline(y=0, color="gray", linestyle=":")
ax.axvline(x=np.pi, color="red", linestyle="--", linewidth=1)
ax.axhspan(0, 0.5, alpha=0.1, color="green")
Colors, Styles & Themes
# Set a built-in style
plt.style.use("seaborn-v0_8-whitegrid") # or "ggplot", "fivethirtyeight", etc.
# Colormaps for continuous data
cmap = plt.cm.viridis # sequential
cmap = plt.cm.RdBu # diverging
cmap = plt.cm.tab10 # categorical
# Color cycles for multiple lines
from matplotlib.cm import get_cmap
colors = get_cmap("tab10").colors
# Custom rcParams
plt.rcParams.update({
"font.family": "sans-serif",
"font.size": 12,
"axes.titlesize": 14,
"figure.dpi": 150,
})
Colorbars
sc = ax.scatter(x, y, c=y, cmap="plasma", s=50)
cbar = fig.colorbar(sc, ax=ax)
cbar.set_label("Magnitude")
Twin Axes (dual y-axis)
ax2 = ax.twinx()
ax2.plot(x, np.cos(x), color="darkorange", label="cos(x)")
ax2.set_ylabel("cos(x)", color="darkorange")
ax2.tick_params(axis="y", labelcolor="darkorange")
3D Plots
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
X, Y = np.meshgrid(np.linspace(-3, 3, 50), np.linspace(-3, 3, 50))
Z = np.sin(np.sqrt(X**2 + Y**2))
ax.plot_surface(X, Y, Z, cmap="coolwarm", alpha=0.9)
ax.scatter(x_pts, y_pts, z_pts, c="red", s=20)
Animations
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
line, = ax.plot([], [], lw=2)
ax.set_xlim(0, 2 * np.pi)
ax.set_ylim(-1, 1)
def update(frame):
x_data = np.linspace(0, 2 * np.pi, 100)
y_data = np.sin(x_data + frame * 0.1)
line.set_data(x_data, y_data)
return line,
anim = FuncAnimation(fig, update, frames=100, interval=50, blit=True)
anim.save("animation.gif", writer="pillow", fps=20)
Saving Figures
fig.savefig("chart.png", dpi=150, bbox_inches="tight")
fig.savefig("chart.svg") # vector format
fig.savefig("chart.pdf") # publication-ready
plt.close(fig) # free memory — always close after saving
Integration with Data Libraries
With NumPy
x = np.arange(10)
y = np.random.randn(10).cumsum()
ax.plot(x, y)
With Pandas
import pandas as pd
df = pd.DataFrame({"x": range(10), "y": np.random.randn(10)})
df.plot(x="x", y="y", ax=ax) # pandas .plot() uses Matplotlib under the hood
With Polars
import polars as pl
df = pl.DataFrame({"x": range(10), "y": np.random.randn(10)})
ax.plot(df["x"].to_numpy(), df["y"].to_numpy())
Best Practices
- Always use the OO API (
fig, ax = plt.subplots()) — avoidplt.plot()directly. - Close figures after saving:
plt.close(fig)prevents memory leaks in loops. - Use
constrained_layout=Trueinstead oftight_layout()— more robust. - Export vector formats (SVG, PDF) for publications; PNG at ≥150 DPI for web.
- Avoid excessive grid lines and chartjunk — remove top/right spines for clean look.
- Set figure DPI globally via
rcParams["figure.dpi"]for consistent rendering. - Use semantic color names or hex codes rather than single-letter shortcuts.
- Label everything: title, x-axis, y-axis, legend entries, and colorbar.
Common Plot Patterns
Side-by-side comparison
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5), layout="constrained")
ax1.plot(x, np.sin(x)); ax1.set_title("sin(x)")
ax2.plot(x, np.cos(x)); ax2.set_title("cos(x)")
Shared axes
fig, axes = plt.subplots(3, 1, figsize=(8, 9), sharex=True, layout="constrained")
Figure-level legend
handles, labels = ax.get_legend_handles_labels()
fig.legend(handles, labels, loc="lower center", ncol=3, bbox_to_anchor=(0.5, -0.05))