Skip to content

Matplotlib 速查表

Python 数据可视化的综合绘图库。

01

入门

安装与第一个图形

pip install matplotlib 安装完整的 matplotlib 发行版(包含 pyplot 接口)。约定用 import matplotlib.pyplot as plt,几乎所有教程和示例都使用此别名。plt.show() 会阻塞直到关闭窗口;在 Jupyter 中 %matplotlib inline 将图形内嵌到输出单元格,%matplotlib widget 则启用交互式图形。

matplotlib
# install
pip install matplotlib

import matplotlib.pyplot as plt
import numpy as np

# a simple line plot
x = np.linspace(0, 10, 100)
y = np.sin(x)

plt.plot(x, y)
plt.title("Sine Wave")
plt.xlabel("x")
plt.ylabel("sin(x)")
plt.show()

pyplot 状态式接口

pyplot 是一个状态机接口:它跟踪'当前图形'和'当前坐标轴',plt.plot() 等函数会作用于当前坐标轴,没有则自动创建一个。这种风格适合快速原型和交互式使用,但在程序化代码中面向对象的风格(fig, ax = plt.subplots())更清晰、更可控,尤其是在处理多个子图时。

matplotlib
import matplotlib.pyplot as plt

# pyplot tracks the "current" figure and axes
plt.figure()             # create a new figure
plt.plot([1, 2, 3], [4, 5, 2])
plt.title("State-based")  # applies to the current axes
plt.xlim(0, 4)
plt.ylim(0, 6)
plt.show()

# plt.gcf() -> current figure, plt.gca() -> current axes
print(plt.gca())

Figure 与 Axes(面向对象接口)

Figure 是顶层容器,容纳一个或多个 Axes、颜色条、图例、文本等。Axes 是带有坐标轴的实际绘图区域。面向对象风格(用 ax.plot() 而非 plt.plot())显式控制哪个坐标轴,这是库代码和复杂图形的推荐风格。一个 Figure 可包含多个用 add_subplot 或 subplots 添加的 Axes。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

# object-oriented interface: explicit Figure and Axes objects
fig, ax = plt.subplots()      # fig is the canvas, ax is a single Axes
x = np.linspace(0, 5, 50)
ax.plot(x, x**2, label="x^2")
ax.plot(x, x**3, label="x^3")
ax.set_title("OO Interface")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.legend()
plt.show()

常用导入与约定

np.linspace 创建均匀间隔的采样,非常适合折线图。约定:plt 是 pyplot 的标准别名,np 是 numpy 的别名。这两个别名在整个生态系统中是通用的——保持使用它们可让你的代码可读且对其他人友好。arr.shape 检查数组的维度;大多数绘图函数接受 1-D 数组并按顺序配对 (x, y) 值。

matplotlib
# the universal alias
import matplotlib.pyplot as plt
import numpy as np

# for the OO interface you often see
from matplotlib.figure import Figure
from matplotlib.axes import Axes

# set a global style once
plt.rcParams["figure.dpi"] = 100
plt.rcParams["font.size"] = 11

# inline backend in Jupyter (not needed in scripts)
# %matplotlib inline

后端与 plt.show()

matplotlib 通过'后端'将图形渲染到屏幕或文件。交互式后端(Qt5Agg、TkAgg、MacOSX)打开 GUI 窗口;内联后端(SVG、notebook)在 Jupyter 中渲染。plt.show() 触发 GUI 事件循环并显示所有打开的图形——它应该通常是脚本的最后一行。plt.savefig() 在 show() 之前调用,否则图形已被清空。

matplotlib
import matplotlib

# list available backends
print(matplotlib.rcsetup.all_backends)

# force a backend (do this BEFORE importing pyplot)
# matplotlib.use("Agg")        # non-interactive, for saving files
# matplotlib.use("TkAgg")      # interactive Tk window

import matplotlib.pyplot as plt
plt.plot([1, 2, 3])
# plt.show() blocks until the window is closed
plt.savefig("out.png")  # saving works without any GUI backend
02

折线图 (plot)

基础折线图

plt.plot(x, y) 是最基础的绘图命令——用直线段连接 (x, y) 点。如果只传入一个数组,它被视为 y 值,x 默认为 0..N-1 的整数索引。plot 返回 Line2D 对象列表,你可以用它们在之后修改属性(如 line.set_color())。ax.plot() 在面向对象接口中做同样的事。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.arange(0, 10, 0.5)
y = x * 2

plt.plot(x, y)
plt.title("y = 2x")
plt.xlabel("x")
plt.ylabel("y")
plt.grid(True)
plt.show()

多条线

多次调用 plot 会在同一坐标轴上叠加线条,matplotlib 自动循环默认颜色。label= 关键字为每条线命名,plt.legend() 显示图例。ax.plot 支持一次性传入多对 (x, y, fmt) 参数:ax.plot(x, y1, 'r-', x, y2, 'b--')——对紧凑代码很方便。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 2 * np.pi, 100)
plt.plot(x, np.sin(x), label="sin")
plt.plot(x, np.cos(x), label="cos")
plt.plot(x, np.sin(x) + np.cos(x), label="sin+cos")
plt.legend()
plt.title("Trig Functions")
plt.show()

线型与标记

格式字符串 fmt = '[color][marker][line]' 是从 MATLAB 继承的简写:'r-' 是红色实线,'go' 是绿色圆点(无线),'k--' 是黑色虚线。也可用关键字 linestyle='--'、marker='o'、color='red' 等单独设置,在代码中更易读。markersize 控制标记大小,markevery 只在第 N 个点画标记以避免拥挤。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 5, 20)
# format string: [color][marker][linestyle]
plt.plot(x, x, "r-")        # red solid line
plt.plot(x, x*2, "go--")    # green circles, dashed
plt.plot(x, x*3, "bs:")     # blue squares, dotted
plt.plot(x, x*4, "k^-.")    # black triangles, dash-dot

# explicit kwargs (clearer, recommended)
plt.plot(x, x*5, color="purple", marker="D",
         linestyle="-.", linewidth=2, markersize=6)

颜色

matplotlib 接受多种颜色格式:单字符('r','g','b','k','w','c','m','y')、名称('crimson','steelblue')、十六进制('#11557C')、RGB 元组 ((0,0,0) 到 (1,1,1))、CSS4 名称以及 xkcd 颜色调查名称('xkcd:sky blue')。CN 格式('C0' 到 'C9')引用默认颜色循环,适合与样式无关的代码。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 1, 30)
plt.plot(x, x, color="red")              # named color
plt.plot(x, x+0.2, color="#FF8800")      # hex RGB
plt.plot(x, x+0.4, color=(0.1, 0.8, 0.2))# RGB tuple 0-1
plt.plot(x, x+0.6, color="C0")           # cycle color 0
plt.plot(x, x+0.8, color="tab:purple")   # Tableau palette
# gray shorthand: a string of float in [0,1]
plt.plot(x, x+1.0, color="0.5")          # mid gray
plt.show()

线宽、Alpha 与 zorder

linewidth(lw)是线宽(以点为单位,默认 1.5)。alpha 设置透明度(0=完全透明,1=完全不透明)——在数据点重叠时显示密度很有用。zorder 控制绘制顺序:zorder 值高的艺术家画在低的之上,所以把它们放在网格、填充和其他线之上。默认值:图像=1,线条=2,标记=3,文本=3。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
# alpha = transparency (0 invisible, 1 opaque)
# zorder = draw order; higher draws on top
plt.fill_between(x, np.sin(x), alpha=0.3, zorder=1)
plt.plot(x, np.sin(x), linewidth=2, zorder=2)
plt.plot(x, np.cos(x), linewidth=4, alpha=0.7, zorder=3)
plt.show()

阶梯图与楼梯图

对于阶梯式数据(如数字采样信号),plot 的 where='pre'/'post'/'mid' 选项控制阶梯的水平段相对于 x 值的对齐方式。ax.step 是显式等价物。stairs 直接从预计算的 bin 边缘绘制——它是直方图风格可视化的推荐方法,比用步骤语义绘制 bar 更高效且不易出错。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.arange(5)
y = [1, 3, 2, 4, 3]

# step: like plot but with horizontal/vertical steps
plt.step(x, y, where="post", label="post")
plt.step(x, y + 1, where="mid", label="mid")
plt.step(x, y + 2, where="pre", label="pre")
plt.legend(title="where=")

# stairs (matplotlib 3.4+): step from explicit bin edges
plt.stairs([1, 2, 1], baseline=0)

plt.show()
03

散点图

基础散点图

ax.scatter(x, y) 绘制圆形标记,在低密度时与 plot(x, y, 'o') 等价。但当每个点需要独立的大小时 scatter 更强:它接受与 x/y 长度相同的 s= 和 c= 数组,使你能按数据维度缩放标记大小和颜色。在密集数据下 scatter 比 plot('o') 慢——在数万点时考虑后者。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(0)
x = rng.random(50)
y = rng.random(50)

plt.scatter(x, y)
plt.title("Basic Scatter")
plt.xlabel("x")
plt.ylabel("y")
plt.axis("equal")   # equal aspect ratio
plt.show()

标记大小与颜色

s= 是以点为单位的标记面积(平方点),所以 s=100 是约 10x10 点的标记;c= 设置颜色。传递数组时,cmap 决定如何将值映射到颜色;vmin/vmax 固定颜色范围(适合跨多个图形比较)。alpha=0.5 让重叠可见。注意 s 是面积——标记的视觉直径按 sqrt(s) 缩放。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(1)
n = 50
x = rng.random(n)
y = rng.random(n)
sizes = rng.random(n) * 500     # area in points^2
colors = rng.random(n)          # mapped to colormap

plt.scatter(x, y, s=sizes, c=colors, cmap="viridis", alpha=0.6)
plt.colorbar(label="value")
plt.title("Bubble Chart")
plt.show()

颜色映射散点图

在 c= 传入数值数组并用 cmap= 选择颜色映射时,scatter 会将每个值映射到一个颜色——非常适合展示第三个维度。fig.colorbar(sc) 添加图例条。对于离散步长(如类别),使用离散颜色映射或列出边界。viridis 和 plasma 是感知均匀的,对色盲友好且在灰度打印中也能保留顺序信息。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 200)
y = np.sin(x)
c = x           # color by x value

sc = plt.scatter(x, y, c=c, cmap="plasma", vmin=0, vmax=10)
cb = plt.colorbar(sc)
cb.set_label("x position")
plt.title("Colored by x")
plt.show()

透明度与过密绘制

当散点密集时,实心标记会相互遮挡(过密绘制)。降低 alpha 到 0.3-0.5 让重叠可见——颜色越深表示密度越高。对于非常密集的数据切换到 hexbin 或 hist2d 以显示二维密度。rasterized=True 将散点光栅化为图像而非向量路径——能大幅减小密集散点图的 PDF/SVG 文件大小。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(2)
n = 5000
x = rng.normal(0, 1, n)
y = rng.normal(0, 1, n)

# dense scatter: low alpha reveals density
plt.scatter(x, y, s=5, alpha=0.05, c="navy")
plt.title("Density via alpha")

# alternative: hexbin for very large datasets
plt.figure()
plt.hexbin(x, y, gridsize=40, cmap="Blues")
plt.colorbar(label="count")
plt.show()

分类散点图

matplotlib 3.x 可直接为 x 传入字符串列表,自动用整数位置和字符串刻度标签绘制。这避免了手动用列表推导式枚举类别。对于带每个类别分布的统计图,Seaborn 的 stripplot/swarmplot/violinplot 提供更丰富的统计可视化,使用类似分类 API。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(3)
groups = ["A", "B", "C"]
data = [rng.normal(0, 1, 30),
        rng.normal(2, 1, 30),
        rng.normal(1, 1.5, 30)]

for i, d in enumerate(data):
    # add jitter on x so points do not stack on one line
    jitter = rng.uniform(-0.1, 0.1, len(d))
    plt.scatter(np.full(len(d), i) + jitter, d, label=groups[i])

plt.xticks(range(len(groups)), groups)
plt.legend()
plt.show()
04

柱状图

垂直柱状图

ax.bar(x, heights) 绘制垂直柱,顶部在 heights。x 通常是类别位置(数字或字符串)。width 默认 0.8;在并排分组柱图中,通过 +/- 偏移并使用更小的 width(如 0.35)来避免重叠。返回 BarContainer,允许后续修改单个柱的属性(如为突出显示某个而着色)。

matplotlib
import matplotlib.pyplot as plt

categories = ["Apple", "Banana", "Cherry", "Date"]
values = [25, 40, 30, 15]

bars = plt.bar(categories, values, color="tab:blue", edgecolor="black")

# annotate each bar with its value
for bar, v in zip(bars, values):
    plt.text(bar.get_x() + bar.get_width()/2, v + 1,
             str(v), ha="center", va="bottom")

plt.title("Fruit Sales")
plt.ylabel("units sold")
plt.ylim(0, 50)
plt.show()

水平柱状图

barh(y, widths) 绘制水平柱;在类别名称较长时优于 bar,因为标签水平显示且无需旋转。y 位置默认为 0..N-1 的整数;set_yticks 和 set_yticklabels 设置类别标签。注意 width 是这里的柱长,height 才是柱的粗细(垂直厚度)——命名容易混淆。

matplotlib
import matplotlib.pyplot as plt

items = ["Reading", "Coding", "Sleep", "Exercise"]
hours = [2, 6, 8, 1]

plt.barh(items, hours, color="tab:green")
plt.xlabel("hours per day")
plt.title("Daily Activities")

# invert so the first item appears at the top
plt.gca().invert_yaxis()
plt.show()

分组柱状图

并排分组柱:为每个系列计算偏移位置(x +/- n*width),用更小的 width(如 0.35)调用 bar 多次。set_xticks(x) 将刻度放在分组中心;将图例句柄和标签传递给 ax.legend()。这种手动偏移是标准做法——matplotlib 没有内置的分组柱图 API,但模式很直接。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

labels = ["Q1", "Q2", "Q3", "Q4"]
a = [20, 35, 30, 35]
b = [25, 32, 34, 20]
x = np.arange(len(labels))
w = 0.35

plt.bar(x - w/2, a, w, label="2023")
plt.bar(x + w/2, b, w, label="2024")
plt.xticks(x, labels)
plt.ylabel("revenue")
plt.legend()
plt.show()

堆叠柱状图

堆叠柱:第一次正常调用 bar;后续调用传入 bottom=previous_totals 来堆叠在上面。返回的 BarContainer 可用于在每个柱段中心添加标签。edgecolor='white' 在彩色柱段间添加细白线,提高可读性。确保所有系列对齐到相同的 x 位置,且各段的 bottom 累加。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

labels = ["A", "B", "C"]
men = [20, 35, 30]
women = [25, 32, 34]

plt.bar(labels, men, label="Men")
plt.bar(labels, women, bottom=men, label="Women")
plt.legend()
plt.title("Stacked Bars")
plt.show()

带误差棒的柱状图

bar/barh 接受 yerr(或 xerr)= 标量/数组以为每个柱添加误差棒。capsize 加上水平端帽;error_kw 字典传递给误差棒 Line2D(ecolor、elinewidth、capthick)。对于不对称误差,传入 shape (2, N) 的数组,行 0 为下界、行 1 为上界。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

labels = ["A", "B", "C"]
means = [20, 35, 30]
errors = [2, 4, 3]

plt.bar(labels, means, yerr=errors, capsize=6,
        color="tab:orange", edgecolor="black",
        error_kw={"ecolor": "black", "linewidth": 1.5})
plt.title("Bar with Error Bars")
plt.ylabel("mean value")
plt.show()
05

直方图

基础直方图

plt.hist(data, bins=N) 将数据分入 N 个等宽箱并绘制柱状图。bins 接受整数(自动等宽箱)、箱边数组或字符串策略('auto'、'fd'、'scott' 等)。density=True 将计数归一化为概率密度(总面积=1),使其可与 PDF 曲线叠加。返回 (counts, bin_edges, patches) 三元组——patches 可用于设置单个柱的属性。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(0)
data = rng.normal(0, 1, 1000)

plt.hist(data, bins=30)
plt.title("Histogram")
plt.xlabel("value")
plt.ylabel("frequency")
plt.show()

选择箱数

箱数选择很重要:太少会隐藏形状(欠平滑),太多会显示噪声(过平滑)。'auto' 策略(Freedman-Diaconis)会根据数据量和分布自适应。range=(low, high) 限制分箱范围——在数据有离群值时很有用。对于分类数据使用 bar 而非 hist;hist 是为连续数值分布设计的。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(1)
data = rng.normal(0, 1, 1000)

# auto bin selection (Freedman-Diaconis, Sturges, etc.)
plt.hist(data, bins="auto", alpha=0.5, label="auto")
plt.hist(data, bins=10, alpha=0.5, label="10 bins")
plt.hist(data, bins=np.arange(-4, 4, 0.25),
         alpha=0.5, label="0.25 width")
plt.legend()
plt.show()

多个直方图

多次调用 hist 会叠加直方图。设置 alpha<1(如 0.5)使重叠区域可见。histtype='stepfilled' 移除内部边缘,使叠加更易读。density=True 归一化每条曲线,所以不同大小的数据集可在同一尺度上比较。stacked=True 改为堆叠而非叠加。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(2)
a = rng.normal(0, 1, 1000)
b = rng.normal(1, 1.2, 1000)

plt.hist(a, bins=30, alpha=0.6, label="A", color="tab:blue")
plt.hist(b, bins=30, alpha=0.6, label="B", color="tab:orange")
plt.legend()
plt.show()

# stacked alternative
plt.figure()
plt.hist([a, b], bins=30, stacked=True, label=["A", "B"])
plt.legend()
plt.show()

密度 / 归一化

density=True 将计数转换为概率密度(每柱高度 = 计数/(总数 * 箱宽)),所以所有柱的面积之和为 1——这使直方图可与概率密度函数曲线(如正态分布)叠加比较。这与 density=False(原始计数)对比,后者只显示绝对频率。在比较不同大小样本的分布形状时总是使用 density=True。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(3)
data = rng.normal(0, 1, 1000)

# density=True normalizes the area to 1 (PDF)
plt.hist(data, bins=30, density=True, alpha=0.6)

# overlay the theoretical PDF
from scipy.stats import norm
xs = np.linspace(-4, 4, 200)
plt.plot(xs, norm.pdf(xs), "r-", lw=2)
plt.title("Normalized Histogram + PDF")
plt.show()

二维直方图

hist2d 将 (x, y) 点分入二维网格并显示为热力图——比密集散点图更好(避免过密绘制)。cmin=1 隐藏空单元(默认显示为背景色)。bins 接受整数或 (nx, ny) 元组。返回 (counts, xedges, yedges, QuadMesh)——后者传递给 colorbar 以显示计数图例。对于非矩形分箱使用 hexbin。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(4)
x = rng.normal(0, 1, 10000)
y = x + rng.normal(0, 1, 10000)

plt.hist2d(x, y, bins=40, cmap="Blues")
plt.colorbar(label="count")
plt.title("2D Histogram")
plt.xlabel("x")
plt.ylabel("y")
plt.show()
06

饼图

基础饼图

ax.pie(sizes) 绘制饼图,扇形面积与 sizes 成正比。labels= 在每个扇形外侧添加文字;autopct='%1.1f%%' 在扇形内显示百分比。startangle=90 让第一个扇形从顶部开始(默认 0=右侧),逆时针排列。返回 (wedges, texts, autotexts)——其中 autotexts 是百分比标签,可单独设置样式。

matplotlib
import matplotlib.pyplot as plt

labels = ["Rent", "Food", "Transport", "Fun"]
sizes = [1200, 600, 300, 400]

plt.pie(sizes, labels=labels, autopct="%1.1f%%", startangle=90)
plt.title("Monthly Budget")
plt.axis("equal")   # circle, not ellipse
plt.show()

分离与阴影

explode 接受与 sizes 长度相同的偏移列表,把对应扇形向外拉出(0=无偏移,0.1=约 10% 半径)。shadow=True 添加伪 3D 阴影效果。对于现代数据可视化,饼图通常不推荐(人眼对角度的判断不如长度准确)——但小数量类别(<6)且需直观占比展示时仍可用。

matplotlib
import matplotlib.pyplot as plt

labels = ["A", "B", "C", "D"]
sizes = [30, 25, 25, 20]
explode = (0, 0.1, 0, 0)   # pull the 2nd wedge out

plt.pie(sizes, labels=labels, explode=explode,
        shadow=True, autopct="%1.0f%%",
        startangle=140)
plt.title("Exploded Pie")
plt.show()

环形图

wedges, _ = ax.pie(...) 之后用 set_width(0.4) 把每个扇形的'厚度'(从外径向内)缩小,留出中心空间形成环形。然后在中心用 ax.text(0,0,...) 写汇总值(如总数)。环形图通常比传统饼图更易读,因为视觉权重在外弧长度而非角度。

matplotlib
import matplotlib.pyplot as plt

labels = ["A", "B", "C"]
sizes = [40, 35, 25]

wedges, texts = plt.pie(sizes, labels=labels,
                        wedgeprops=dict(width=0.4, edgecolor="w"))
plt.title("Donut Chart")
# add center text
plt.text(0, 0, "Total\n100", ha="center", va="center", fontsize=14)
plt.show()

百分比与起始角

autopct 接受格式字符串('%1.1f%%'、'%.0f%%')或返回字符串的可调用函数,使其能显示绝对值+百分比。counterclock=False 使扇形按顺时针排列;startangle=90 让第一片从顶部开始(更符合直觉的'12 点钟方向')。pctdistance 和 labeldistance 控制百分比/标签文字距中心的相对位置(0-1)。

matplotlib
import matplotlib.pyplot as plt

labels = ["North", "South", "East", "West"]
sizes = [32, 24, 28, 16]
colors = ["#4C72B0", "#DD8452", "#55A868", "#C44E52"]

plt.pie(sizes, labels=labels, colors=colors,
        autopct=lambda p: f"{p:.1f}%\n({p*sum(sizes)/100:.0f})",
        startangle=90, counterclock=False,
        pctdistance=0.75)
plt.show()

嵌套饼图(同心)

在同一 ax 上调用 pie 两次,通过 radius= 和 width= 参数分离内外环——内环小半径、外环大半径。这是展示层级分类(如大类-子类)的紧凑方式。确保两个数据集的扇形对齐(相同 startangle/counterclock),或用显式 wedgeprops 控制每环的边缘宽度。

matplotlib
import matplotlib.pyplot as plt

# outer ring: regions, inner ring: sub-categories
outer = [40, 30, 30]
inner = [20, 20, 15, 15, 25, 5]
outer_labels = ["A", "B", "C"]
inner_labels = ["A1", "A2", "B1", "B2", "C1", "C2"]

plt.pie(outer, labels=outer_labels, radius=1.0,
        wedgeprops=dict(width=0.3, edgecolor="w"))
plt.pie(inner, labels=inner_labels, radius=0.7,
        wedgeprops=dict(width=0.3, edgecolor="w"))
plt.title("Nested Pie")
plt.show()
07

子图与布局

plt.subplot

plt.subplot(nrows, ncols, index) 创建或选中一个虚拟网格中的第 index 个子图(1-based)。该 API 来自 MATLAB,适合简单的等分网格。返回 Axes 对象。对于复杂或不等大小的布局,使用 GridSpec 或 subplots/mosaic。注意 index 从 1 开始(不是 0)——这是常见的错误来源。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 5, 50)

# subplot(nrows, ncols, index)  -- 1-indexed!
plt.subplot(2, 2, 1)
plt.plot(x, x)

plt.subplot(2, 2, 2)
plt.plot(x, x**2)

plt.subplot(2, 2, 3)
plt.plot(x, x**3)

plt.subplot(2, 2, 4)
plt.plot(x, np.sin(x))

plt.tight_layout()
plt.show()

plt.subplots

plt.subplots(n, m) 一次性创建 Figure 和 Axes 数组——这是现代推荐方式。返回 (fig, axs),其中 axs 是 2-D 数组(当 n,m>1)、1-D 数组(当其中之一为 1)或单个 Axes(当都为 1)。squeeze=False 强制始终返回 2-D 数组,简化遍历代码。axs.flat 提供一个 1-D 迭代器。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 2*np.pi, 100)

# one figure, 2x2 grid of Axes, shared x and y
fig, axes = plt.subplots(2, 2, figsize=(8, 6),
                         sharex=True, sharey=True)

axes[0, 0].plot(x, np.sin(x)); axes[0, 0].set_title("sin")
axes[0, 1].plot(x, np.cos(x)); axes[0, 1].set_title("cos")
axes[1, 0].plot(x, np.tan(x)); axes[1, 0].set_title("tan")
axes[1, 1].plot(x, -np.sin(x)); axes[1, 1].set_title("-sin")

fig.suptitle("Trig Family")
plt.tight_layout()
plt.show()

GridSpec

GridSpec 让子图跨越多行/多列,实现不等大小布局。fig.add_subplot(gs[0, :]) 创建一个跨越所有列的子图;gs[1, :-1] 跨越除最后一列外的所有列。width_ratios/height_ratios 控制列/行的相对大小。plt.subplot_mosaic 是更新的便利 API,用字符矩阵描述布局(每个唯一字符=一个子图)。

matplotlib
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

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

ax1 = fig.add_subplot(gs[0, :])     # full top row
ax2 = fig.add_subplot(gs[1, :2])    # middle, left 2 cols
ax3 = fig.add_subplot(gs[1:, 2])    # right, spans 2 rows
ax4 = fig.add_subplot(gs[2, 0:2])   # bottom-left

ax1.set_title("wide top")
ax3.set_title("tall right")
plt.tight_layout()
plt.show()

共享坐标轴

sharex/sharey='all' 链接所有子图的缩放/平移——在一个上缩放会同步所有其他。'col'/'row' 仅在列/行内共享。共享时内部刻度标签会自动隐藏以减少杂乱。在比较多个数据集时这至关重要,否则不同子图的尺度可能误导读者。sharex=True 也确保相同 xlim,使柱图等视觉上可比。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True)

ax1.plot(x, np.sin(x))
ax2.plot(x, np.cos(x))

# zooming on one now zooms both
ax1.set_xlim(0, 5)
# decouple later if needed
# ax2.get_shared_x_axes().remove(ax2)

fig.suptitle("Shared x-axis")
plt.show()

紧凑布局与约束布局

tight_layout() 自动调整子图参数以避免标签/标题重叠——简单可靠。constrained_layout(在 subplots 中传入 constrained_layout=True 或 rcParam)是更新的替代方案,能正确处理颜色条、图例和 suptitle,且在多次 add_axes 调用后仍能更新。优先使用 constrained_layout 而非手动 subplots_adjust。

matplotlib
import matplotlib.pyplot as plt

# tight_layout: simple, adjusts subplot spacing
fig, axes = plt.subplots(2, 2)
for ax in axes.flat:
    ax.set_xlabel("x label")
    ax.set_ylabel("y label")
    ax.set_title("a title")
fig.suptitle("tight_layout")
plt.tight_layout()
# leave room for suptitle
plt.tight_layout(rect=[0, 0, 1, 0.95])
plt.show()

# constrained_layout: recomputes on every draw (more robust)
fig, axes = plt.subplots(2, 2, layout="constrained")
fig.suptitle("constrained_layout")
plt.show()
08

图例与标注

基础图例

ax.legend() 收集所有带 label= 的艺术家并显示图例。默认位置 loc='best' 会自动找最少重叠的位置。显式 loc 字符串('upper right'、'lower left' 等)或 2-元组坐标 (x, y) 可固定位置。ncol= 让图例分多列显示,适合多个条目。frameon=False 移除边框,使图例更轻量。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 1, 30)
plt.plot(x, x, label="linear")
plt.plot(x, x**2, label="quadratic")
plt.plot(x, x**3, label="cubic")
plt.legend()
plt.show()

# legend outside the axes
plt.figure()
plt.plot(x, x, label="linear")
plt.plot(x, x**2, label="quadratic")
plt.legend(loc="upper left", bbox_to_anchor=(1, 1))
plt.tight_layout()
plt.show()

图例位置与列数

loc 接受字符串('upper left' 等)或位置代码(0-10),loc='best'(0)自动选择。bbox_to_anchor=(x, y) 配合 loc= 将图例锚定到任意点(坐标在 axes/figure 分数中)。ncol= 在水平方向上分多列显示条目。framealpha、edgecolor、facecolor 控制图例框的外观。mode='expand' 让图例水平拉伸填满 bbox。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 1, 30)
for i in range(1, 6):
    plt.plot(x, x**i, label=f"x^{i}")

plt.legend(loc="upper center", ncol=3, fontsize=9,
           frameon=True, framealpha=0.9,
           title="Powers", title_fontsize=10)
plt.title("Custom Legend")
plt.show()

标注点

ax.annotate(text, xy=(x, y)) 在数据坐标 (x, y) 处放置文本。xytext= 指定文本位置(默认在 xy 处),textcoords= 解释 xytext 的坐标系('offset points' 是常用选择,让标签偏移而不被点遮挡)。arrowprops= 字典(如 dict(arrowstyle='->'))画一条从 xytext 到 xy 的箭头。

matplotlib
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)

# mark and label the maximum
idx = np.argmax(y)
plt.annotate(f"max = {y[idx]:.2f}",
             xy=(x[idx], y[idx]),        # point to annotate
             xytext=(x[idx]-1, 0.5),     # text position
             arrowprops=dict(arrowstyle="->"))
plt.show()

带箭头的标注

arrowprops=dict(arrowstyle='->', ...) 是标注调用的核心。arrowstyle 接受简写字符串('->'、'-|>'、'<->')或 ArrowStyle 对象。connectionstyle='arc3,rad=0.3' 使箭头弯曲;shrinkA/shrinkB 在两端留空隙,使箭头不直接触碰文字或点。xycoords 控制目标点坐标系(默认 'data')。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-3, 3, 100)
plt.plot(x, x**3 - 3*x)

plt.annotate("local min", xy=(1, -2), xytext=(2, -10),
             arrowprops=dict(arrowstyle="->", color="red",
                             lw=2, connectionstyle="arc3,rad=0.3"))
plt.annotate("local max", xy=(-1, 2), xytext=(-3, 8),
             arrowprops=dict(arrowstyle="-|>", color="blue",
                             connectionstyle="angle3"))
plt.show()

自定义图例句柄

ax.legend(handles, labels) 接受显式艺术家列表而非自动收集——用于精确控制图例条目或为图中未出现的'代理'艺术家创建图例(如展示用 Line2D([], [], color='red', label='trend line') 表示的分类)。这也可重新排序图例或合并多个艺术家的标签。

matplotlib
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
from matplotlib.patches import Patch

plt.plot([1, 2, 3], [1, 4, 9], "o-", color="tab:blue")
plt.plot([1, 2, 3], [1, 8, 27], "s--", color="tab:orange")

custom = [
    Line2D([0], [0], color="tab:blue", marker="o", label="squared"),
    Line2D([0], [0], color="tab:orange", marker="s", ls="--", label="cubed"),
    Patch(facecolor="gray", label="reference band"),
]
plt.legend(handles=custom)
plt.show()
09

样式、颜色与 rcParams

内置样式

plt.style.use('name') 全局应用一个样式;plt.style.context('name') 在 with 块内临时应用而不影响全局。print(plt.style.available) 列出所有样式。'seaborn-v0_8-*' 系列在 matplotlib 3.6+ 中内置(原 seaborn 样式名);'ggplot'、'bmh'、'fivethirtyeight' 模仿各自的设计美学。dark_background 适合暗色主题演示。

matplotlib
import matplotlib.pyplot as plt

# list all available styles
print(plt.style.available)

# use a style globally
plt.style.use("seaborn-v0_8-whitegrid")

# or as a context manager (temporary)
with plt.style.context("dark_background"):
    plt.figure()
    plt.plot([1, 2, 3], [1, 4, 9])
    plt.title("dark")
    plt.show()

plt.plot([1, 2, 3], [9, 4, 1])
plt.title("default again")
plt.show()

颜色映射

Colormap 决定数值如何映射到颜色。三类:(1) 顺序(如 viridis、plasma)——单色递进,适合低到高的连续数据;(2) 发散(如 coolwarm、RdBu)——双色中间有中性点,适合围绕中心的数据;(3) 定性(如 Set1、tab10)——离散颜色,无顺序含义。viridis 是 matplotlib 默认且是感知均匀、色盲友好的。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

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

# sequential, diverging, qualitative
for cmap in ["viridis", "plasma", "coolwarm", "RdBu", "Set2"]:
    plt.scatter(x, y, c=x, cmap=cmap, label=cmap)
    plt.colorbar(label=cmap)
    plt.title(cmap)
    plt.show()

自定义颜色循环

rcParams['axes.prop_cycle'] 决定 plot 时自动分配的颜色顺序。用 cycler.cycler(color=[...]) 替换以自定义循环。循环可包含多个属性(如 color + linestyle),使每个新系列获得独特的颜色和线型组合。这比在每个 plot 调用中重复指定 color= 更简洁。

matplotlib
import matplotlib.pyplot as plt
from cycler import cycler

# set the default color cycle for new axes
plt.rcParams["axes.prop_cycle"] = cycler(
    color=["#e41a1c", "#377eb8", "#4daf4a", "#984ea3", "#ff7f00"])

for i in range(1, 6):
    plt.plot([0, 1, 2], [i]*3, label=f"line {i}", lw=4)

plt.legend()
plt.title("Custom Cycle")
plt.show()

rcParams

rcParams 是全局配置字典,控制所有默认值——图形大小、DPI、字体大小、线宽、颜色循环等。在脚本顶部设置一次以统一样式。plt.rc('figure', figsize=(8,6)) 或 rcParams['figure.figsize'] = [8, 6] 是等价的。用 mpl.rcParams.update({...}) 一次更新多个值。

matplotlib
import matplotlib.pyplot as plt
import matplotlib as mpl

# read / set individual parameters
mpl.rcParams["figure.figsize"] = (8, 4)
mpl.rcParams["figure.dpi"] = 100
mpl.rcParams["font.size"] = 12
mpl.rcParams["axes.grid"] = True
mpl.rcParams["axes.grid.which"] = "both"
mpl.rcParams["savefig.bbox"] = "tight"

# update many at once
plt.rcParams.update({
    "axes.titlesize": "large",
    "axes.labelsize": "medium",
    "lines.linewidth": 2,
})

plt.plot([1, 2, 3])
plt.title("Configured Defaults")
plt.show()

颜色格式

matplotlib 接受的颜色规范:单字符('rgbcmykw')、CSS4 名称('crimson')、十六进制('#FF5733' 或简写 '#F53')、RGB/RGBA 元组(0-1 或 0-255,需在 0-1)、灰度字符串('0.5')、CN 循环引用('C0'-'C9')、xkcd 名称('xkcd:sky blue')、tab: 名称('tab:blue')及 Tableau 颜色。统一用十六进制或命名颜色可保证跨样式一致性。

matplotlib
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors

# all named colors
print(list(mcolors.CSS4_COLORS)[:5])

# convert between formats
print(mcolors.to_hex((0.1, 0.2, 0.5)))      # #1a3380
print(mcolors.to_rgb("#ff8800"))            # (1.0, 0.533, 0.0)

# make a custom colormap from a list of colors
cmap = mcolors.LinearSegmentedColormap.from_list(
    "rg", ["red", "yellow", "green"])
import numpy as np
plt.imshow(np.linspace(0, 1, 256).reshape(1, -1),
           cmap=cmap, aspect="auto")
plt.title("Custom Colormap")
plt.show()
10

坐标轴设置

范围 (xlim / ylim)

set_xlim/set_ylim 强制坐标轴范围;不调用时 matplotlib 自动从数据推导。传入 None(如 set_xlim(0, None))让一端自动。set_xbound 是带优先级的替代。在子图比较时固定范围至关重要,否则不同子图可能显示不同尺度造成误导。invert_xaxis()/invert_yaxis() 反转方向(如 y 轴向下增长)。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
plt.plot(x, np.sin(x))

# set explicit limits
plt.xlim(2, 8)
plt.ylim(-1.5, 1.5)

# or autoscale with margin
# plt.margins(x=0.05, y=0.1)

# query current limits
print(plt.xlim())   # (2.0, 8.0)
plt.show()

刻度

set_xticks(locations) 设置刻度位置;可选 set_xticklabels(labels) 用自定义文字替换数字标签。在 matplotlib 3.5+ 中两者必须分开调用。对于分类数据,set_xticks(range(N)) 配合 set_xticklabels(names) 是标准做法。rotation= 旋转标签以避免长名称重叠。MaxNLocator 自动限制刻度数量以避免拥挤。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 2*np.pi, 100)
plt.plot(x, np.sin(x))

# set tick positions
plt.xticks([0, np.pi/2, np.pi, 3*np.pi/2, 2*np.pi])

# set positions AND labels
plt.xticks([0, np.pi/2, np.pi, 3*np.pi/2, 2*np.pi],
           ["0", "pi/2", "pi", "3pi/2", "2pi"])

# turn off ticks
# plt.xticks([])

plt.show()

刻度格式化

set_xticklabels 接受任意字符串列表,完全控制刻度文字(如将 0/1 替换为 'cat'/'dog')。对于数字格式化,FuncFormatter 接受一个 (x, pos) -> str 函数,或 StrMethodFormatter 用 '{x:.2f}' 风格模板。ScalarFormatter 控制 sci_notation;PercentFormatter 把 0-1 显示为 0-100%。Formatter 作用于主刻度,set_minor_formatter 设置次刻度。

matplotlib
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import MultipleLocator, FormatStrFormatter, FuncFormatter

x = np.linspace(0, 5, 100)
plt.plot(x, x**2)

ax = plt.gca()
ax.xaxis.set_major_locator(MultipleLocator(1))
ax.xaxis.set_minor_locator(MultipleLocator(0.25))
ax.yaxis.set_major_formatter(FormatStrFormatter("%.0f"))

# format with a function
ax.yaxis.set_major_formatter(
    FuncFormatter(lambda v, p: f"{v/1000:.1f}k"))

plt.show()

对数刻度

set_xscale('log') 切换到对数刻度——数据跨数量级时(如 1 到 100000)必备。symlog 是对称版本,可处理 0 和负数(在 0 附近用线性,远离 0 用对数)。LogLocator 自动选择 10 的幂次刻度位置;LogFormatter 用科学记数法显示。注意:对数轴下 set_xlim 必须用正值,且数据不能含 0 或负数。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.logspace(0, 4, 50)   # 10^0 .. 10^4
plt.plot(x, x**2)

# log both axes
plt.xscale("log")
plt.yscale("log")

# symlog: log scale that handles negative/zero values
# plt.yscale("symlog", linthresh=1)

plt.title("Log-Log Plot")
plt.grid(True, which="both", ls="--", alpha=0.5)
plt.show()

宽高比与边框

set_aspect('equal') 让 x、y 单位长度在屏幕上相等(对圆显示为圆而非椭圆)。set_aspect('equal', adjustable='box') 调整轴框,adjustable='datalim' 调整数据范围。spines 是四条边框;set_visible(False) 隐藏某条边(常隐藏 top/right 以简化美学)。移到中心:spines['left'].set_position(('data', 0))。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots()
theta = np.linspace(0, 2*np.pi, 100)
ax.plot(np.cos(theta), np.sin(theta))

# equal data units on x and y (a true circle)
ax.set_aspect("equal")

# hide the top and right spines
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)

# move the remaining spines to the center for a cross-hair
# ax.spines["left"].set_position(("data", 0))
# ax.spines["bottom"].set_position(("data", 0))

plt.show()
11

文本与数学表达式

坐标轴内文本

ax.text(x, y, s) 在数据坐标 (x, y) 处放置字符串 s。transform=ax.transAxes 改为坐标轴分数(0-1,左下为原点),适合与数据范围无关的标签(如右上角标注)。transform=fig.transFigure 是图形分数坐标。返回 Text 对象,可后续修改 set_text/set_position/set_color 等。

matplotlib
import matplotlib.pyplot as plt

plt.plot([0, 1], [0, 1])
# ax.text(x, y, s)  -- x, y in data coords
plt.text(0.5, 0.5, "center of plot", ha="center", va="center",
         fontsize=14, color="red", rotation=30)

# align to axes fraction instead of data coords
plt.text(0.02, 0.98, "top-left", transform=plt.gca().transAxes,
         ha="left", va="top", bbox=dict(boxstyle="round", fc="yellow"))
plt.show()

suptitle 与 figtext

fig.suptitle 在整个图形顶部添加一个总标题(不同于 ax.set_title 是单个坐标轴标题)。fig.text(x, y, s) 在图形分数坐标放置任意文本。在多子图图形中用 suptitle 描述整体主题,用各 ax.set_title 描述子图。tight_layout() 不会为 suptitle 留空间——用 constrained_layout 或 subplots_adjust(top=0.9)。

matplotlib
import matplotlib.pyplot as plt

fig, axes = plt.subplots(1, 2)
axes[0].plot([1, 2, 3])
axes[1].plot([3, 2, 1])

# figure-level title (above all axes)
fig.suptitle("Main Title", fontsize=16, fontweight="bold")

# arbitrary figure-coordinate text
fig.text(0.5, 0.01, "figure footer text", ha="center", fontsize=9)
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
plt.show()

LaTeX 数学 (mathtext)

在 $...$ 之间的文本用 mathtext(内置 LaTeX 子集)渲染——无需安装 LaTeX。支持 \frac、\sum、\int、希腊字母、上下标、矩阵等。$$ 是显示数学(居中独立行)。对于完整 LaTeX 排版,设置 rcParams['text.usetex']=True(需要系统装 LaTeX)——慢但渲染质量更高。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 2*np.pi, 100)
plt.plot(x, np.sin(x))

# mathtext: enclose LaTeX in dollar signs
plt.title(r"$\sin(x)$ vs $x$")
plt.xlabel(r"$x$ (radians)")
plt.ylabel(r"$\sin(x)$")

# fractions, sums, greek letters
plt.text(2, 0.5, r"$\frac{a}{b} + \sum_{i=1}^{n} x_i^2$")
plt.text(2, -0.5, r"$\alpha, \beta, \gamma, \pi, \infty$")
plt.show()

文本属性

每个文本对象接受丰富属性:fontsize、fontweight('bold')、fontstyle('italic')、family('serif'/'sans-serif'/'monospace')、color 以及背景框 bbox 字典。文本对象会被返回,所以可在之后修改(在动画和交互式更新中很有用)。如 t.set_fontsize(24)、t.set_color('crimson')。

matplotlib
import matplotlib.pyplot as plt

t = plt.text(0.5, 0.5, "Styled Text",
             fontsize=18, fontweight="bold",
             fontstyle="italic", family="serif",
             color="darkblue",
             bbox=dict(boxstyle="round,pad=0.5",
                       facecolor="lightyellow", edgecolor="black"))

# animate or update properties later
t.set_fontsize(24)
t.set_color("crimson")

plt.show()

标注 (textcoords)

annotate 的 textcoords 控制 xytext 如何解释:'data'(默认,数据单位)、'offset points'(相对于 xy 的点偏移——适合把标签从点上挪开)、'axes fraction'(在坐标轴内 0-1)、'figure fraction'。混用坐标系让标注在缩放时仍保持可读。xycoords 控制目标点 xy 的坐标系(默认 'data')。

matplotlib
import matplotlib.pyplot as plt

plt.plot([1, 2, 3, 4], [1, 4, 2, 3])

# xy in data coords, xytext in points OFFSET from xy
plt.annotate("offset text", xy=(2, 4),
             xytext=(15, 15), textcoords="offset points",
             arrowprops=dict(arrowstyle="->"))

# xytext in axes fraction
plt.annotate("axes corner", xy=(2, 4),
             xytext=(0.8, 0.8), textcoords="axes fraction",
             arrowprops=dict(arrowstyle="-|>"))

plt.show()
12

保存图形

savefig 基础

savefig 把当前图形写入磁盘。格式从扩展名推断(.png/.jpg/.svg/.pdf/.eps)或用 format= 显式指定。向量格式(SVG、PDF、EPS)可无损缩放;PNG/JPG 是光栅,打印需要高 dpi。必须在 plt.show() 之前调用 savefig,否则图形已被关闭/清空。

matplotlib
import matplotlib.pyplot as plt

plt.plot([1, 2, 3], [4, 5, 2])

# save BEFORE plt.show() -- show() clears the figure
plt.savefig("plot.png")
plt.savefig("plot.pdf")
plt.show()

dpi 与 bbox_inches

dpi 控制光栅分辨率(默认 100;打印用 300)。bbox_inches='tight' 裁剪周围空白以避免图例/标签被切。pad_inches 加少量边距;facecolor 设置保存的背景色。transparent=True 使背景透明(产生带 alpha 通道的 PNG)。这些是控制输出尺寸和质量的关键参数。

matplotlib
import matplotlib.pyplot as plt

plt.plot([1, 2, 3], [1, 4, 9])

# raster formats (pixels)
plt.savefig("plot.png")          # lossless, default
plt.savefig("plot.jpg", quality=95)
plt.savefig("plot.tiff", dpi=200)

# vector formats (scale without pixelation)
plt.savefig("plot.pdf")          # best for publications
plt.savefig("plot.svg")          # editable in Inkscape/Illustrator

# explicit format override
plt.savefig("plot.dat", format="png")

透明背景

transparent=True 把图形补丁 alpha 设为 0,产生带 alpha 通道的 PNG。在非白色背景的幻灯片或网页上叠加图形时很有用。可与 axes facecolor 组合,只让绘图区有颜色而图形背景透明。fig.patch.set_alpha(0.0) 是手动等价写法。

matplotlib
import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(6, 4))
ax.plot([1, 2, 3], [1, 4, 9])

# dpi controls pixel density: width_px = figsize[0] * dpi
plt.savefig("low.png", dpi=72)     #  432 x 288 px
plt.savefig("high.png", dpi=300)   # 1800 x 1200 px

# set a global default DPI
plt.rcParams["savefig.dpi"] = 300
plt.savefig("global.png")

元数据与质量

JPEG 接受 quality(0-100;越高文件越大但越清晰)。PNG 支持 optimize=True(更小文件)和 metadata 字典(Author/Title/Description/Software/CreationDate)。PDF 元数据键包括 'Title'、'Author'、'Subject'、'Keywords'。progressive=True 生成渐进式 JPEG,适合网页逐步加载。

matplotlib
import matplotlib.pyplot as plt

plt.plot([1, 2, 3], [1, 4, 9])
plt.title("With Long Title That Might Be Cut Off")

# tight bbox trims whitespace and keeps labels inside
plt.savefig("tight.png", bbox_inches="tight", pad_inches=0.1)

# trim even more aggressively
plt.savefig("tighter.png", bbox_inches="tight", pad_inches=0)

# preview the tight bounding box
# bbox = plt.savefig("x.png", bbox_inches="tight")  # returns a Bbox

保存所有图形

在循环中生成大量图形时,保存后务必 close(fig) 以避免内存泄漏(图形会留在 pyplot 状态中直到关闭)。get_fignums() 返回所有当前打开图形的编号,如需遍历已有图形。也可用 fig.savefig() 而非 plt.savefig() 以明确指定哪个图形。

matplotlib
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([1, 2, 3], [1, 4, 9])
ax.set_facecolor("#f0f0f0")

# transparent background (alpha channel)
plt.savefig("transparent.png", transparent=True)

# explicit figure face color
plt.savefig("colored.png", facecolor="lightblue", edgecolor="none")

# control which elements are saved
plt.savefig("full.png", facecolor=fig.get_facecolor())
13

3D 绘图 (mplot3d)

3D 坐标轴设置

导入 mpl_toolkits.mplot3d.Axes3D 注册 '3d' 投影。然后向 add_subplot 或 plt.axes 传入 projection='3d'。生成的 Axes3D 提供 plot3D、scatter3D、plot_surface、plot_wireframe、contour3D 等方法。用 ax.set_xlabel/ylabel/zlabel 设置三轴标签。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure()
# add a 3D axes via projection
ax = fig.add_subplot(projection="3d")

# or from subplots
# fig, ax = plt.subplots(subplot_kw={"projection": "3d"})

ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("3D Axes")

plt.show()

3D 折线与散点

ax.plot(x, y, z) 绘制 3D 折线;ax.scatter(x, y, z) 绘制标记(c= 把颜色映射到值,如深度)。默认视角可用 ax.view_init(elev=30, azim=45) 改变,其中 elev 是仰角(度,从 xy 平面向上),azim 是方位角(绕 z 轴旋转)。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(0)
n = 200
x = rng.normal(0, 1, n)
y = rng.normal(0, 1, n)
z = rng.normal(0, 1, n)
c = np.sqrt(x**2 + y**2 + z**2)

fig = plt.figure()
ax = fig.add_subplot(projection="3d")
sc = ax.scatter(x, y, z, c=c, cmap="viridis")
fig.colorbar(sc, label="distance from origin")
ax.set_title("3D Scatter")
plt.show()

曲面图

plot_surface 需要 meshgrid 生成的 2-D 数组。cmap 按 Z 着色;edgecolor='none' 移除默认的黑色线框。alpha<1 使曲面半透明,可看见背面。fig.colorbar 添加颜色标尺。rstride/cstride 控制行/列步长(降采样以提升性能)。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-5, 5, 80)
y = np.linspace(-5, 5, 80)
X, Y = np.meshgrid(x, y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)

fig = plt.figure()
ax = fig.add_subplot(projection="3d")
surf = ax.plot_surface(X, Y, Z, cmap="coolwarm",
                       rstride=1, cstride=1,
                       linewidth=0, antialiased=True)
fig.colorbar(surf, shrink=0.5, aspect=10)
ax.set_title("3D Surface")
plt.show()

线框图与 contour3D

plot_wireframe 只画网格边(比曲面更轻量)。contour3D 把等高线切片投影到 3D 空间。rstride/cstride 控制行/列步长(降采样以提升性能)。对于大型网格降采样很关键,否则线框会过于密集无法辨认。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-3, 3, 30)
y = np.linspace(-3, 3, 30)
X, Y = np.meshgrid(x, y)
Z = X * np.exp(-X**2 - Y**2)

fig = plt.figure()
ax = fig.add_subplot(projection="3d")
ax.plot_wireframe(X, Y, Z, color="black", rstride=1, cstride=1)
ax.set_title("Wireframe")

# 3D contour (filled) projected at the bottom
fig = plt.figure()
ax = fig.add_subplot(projection="3d")
ax.contourf(X, Y, Z, zdir="z", offset=-0.5, cmap="viridis")
ax.set_title("3D Contour")
plt.show()

3D 柱状图

bar3d 接受柱角坐标(x, y, z=底部)加上柱尺寸(dx, dy, dz=高度)。shade=True 根据光照方向添加伪阴影。传入颜色列表可单独着色每个柱。适合在 x-y 网格上展示三维数据(如地理位置上的值)。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure()
ax = fig.add_subplot(projection="3d")

x = np.arange(3)
y = np.arange(3)
X, Y = np.meshgrid(x, y)
X, Y = X.ravel(), Y.ravel()
Z = np.zeros_like(X)
dz = np.array([1, 2, 3, 2, 3, 1, 3, 1, 2])

ax.bar3d(X, Y, Z, dx=0.6, dy=0.6, dz=dz,
         shade=True, color="tab:orange")
ax.set_title("3D Bar Chart")
plt.show()
14

等高线图

基础等高线

contour 绘制 Z 在所选 levels 处的等值线。clabel 在每条等值线上内联标注数值。levels=N 自动选 N 个等值线;levels=[v1, v2, ...] 用显式值。colors 用单一颜色覆盖颜色映射。需要 meshgrid 生成的 2-D X、Y、Z 数组。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

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)

# line contours
cs = plt.contour(X, Y, Z, levels=15, colors="black", linewidths=0.8)
plt.clabel(cs, inline=True, fontsize=8)
plt.title("Contour Lines")
plt.colorbar(cs)
plt.show()

填充等高线

contourf 用颜色填充等值线之间的区域。与 contour(线)组合可同时显示区域和等值曲线。extend='both' 让颜色条延伸到数据范围之外,用 colors.set_under/over 设置超界颜色。这是展示二维标量场(如温度、压力)的标准方法。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-2, 2, 100)
y = np.linspace(-2, 2, 100)
X, Y = np.meshgrid(x, y)
Z = X**2 + Y**2

cf = plt.contourf(X, Y, Z, levels=20, cmap="viridis")
plt.colorbar(cf, label="Z")
plt.title("Filled Contour")
plt.xlabel("X")
plt.ylabel("Y")
plt.show()

对数等高线层级

对于跨数量级的数据,用 np.logspace 生成 levels 并用 norm='log'(或 matplotlib.colors.LogNorm())让颜色按对数缩放。配合 xscale('log')/yscale('log') 实现对数-对数等高线图。这在频谱、能量分布等数据中很常见。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
Z = X * np.exp(-X**2 - Y**2)

cs = plt.contour(X, Y, Z, 10, colors="black")
plt.clabel(cs, inline=True, fontsize=9, fmt="%.2f")
# label only specific levels
# plt.clabel(cs, levels=[-0.1, 0, 0.1])
plt.title("Labeled Contours")
plt.show()

自定义层级等高线

向 levels 传入值列表可在特定 Z 值处绘制等值线。fmt 控制标签格式('%.1f'、'%.2g' 或函数)。颜色列表(与 levels 长度相同)自定义每条线——适合突出某个阈值(如安全限值)。这在工程图中标定关键等值线很有用。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 5, 100)
y = np.linspace(0, 5, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) + np.cos(Y)

cf = plt.contourf(X, Y, Z, cmap="RdBu")

# basic colorbar
cb = plt.colorbar(cf)
cb.set_label("Z value")
cb.set_ticks([-2, -1, 0, 1, 2])

# place colorbar with specific size and padding
# cb = plt.colorbar(cf, fraction=0.046, pad=0.04)

plt.title("Colorbar Example")
plt.show()

等高线标签与影线

hatches 在 contourf 区域上添加纹理图案('/'、'\\'、'+'、'-'、'x'、'o'、'.')——在黑白打印中颜色不可见时 invaluable。传入列表(每个层级带一个)或单字符串用于全部。学术论文中常用影线区分区域。

matplotlib
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import griddata

# scattered (x, y, z) samples
rng = np.random.default_rng(0)
pts = rng.uniform(-2, 2, (200, 2))
z = np.sin(pts[:,0]) * np.cos(pts[:,1])

# build a regular grid
grid_x, grid_y = np.mgrid[-2:2:100j, -2:2:100j]

# interpolate scattered data onto the grid
grid_z = griddata(pts, z, (grid_x, grid_y), method="cubic")

plt.contourf(grid_x, grid_y, grid_z, levels=20, cmap="plasma")
plt.scatter(pts[:,0], pts[:,1], c="white", s=5, alpha=0.5)
plt.colorbar(label="z")
plt.title("Scattered -> Grid -> Contour")
plt.show()
15

图像显示 (imshow)

imshow 基础

imshow 把 2-D 数组显示为图像/热力图。默认原点(第 0 行)在顶部,行向下排列。cmap 设置颜色映射;interpolation='nearest' 得到清晰像素,'bilinear' 得到平滑渐变。colorbar 添加值图例条。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

# a 2D array is shown as a heatmap (color = value)
rng = np.random.default_rng(0)
img = rng.random((10, 10))

plt.imshow(img, cmap="viridis")
plt.colorbar(label="value")
plt.title("imshow of a 2D array")
plt.show()

# a 3D array (H, W, 3 or 4) is shown as an RGB(A) image
rgb = rng.integers(0, 256, (50, 50, 3), dtype=np.uint8)
plt.imshow(rgb)
plt.axis("off")
plt.title("RGB image")
plt.show()

图像原点与范围

默认 imshow 显示数组索引;extent=[left, right, bottom, top] 把像素映射到数据坐标,使刻度匹配。origin='lower' 翻转图像以匹配笛卡尔约定(第 0 行在底部)。aspect='auto' 拉伸图像填满坐标轴;'equal' 保持像素宽高比。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-3, 3, 200)
y = np.linspace(-3, 3, 200)
X, Y = np.meshgrid(x, y)
Z = np.sin(X**2 + Y**2) / (X**2 + Y**2 + 1)

im = plt.imshow(Z, extent=[-3, 3, -3, 3], origin="lower",
                cmap="RdBu", aspect="auto")
cb = plt.colorbar(im)
cb.set_label("intensity")
plt.title("imshow with extent")
plt.show()

非均匀图像 (pcolormesh)

imshow 要求均匀像素。对于非均匀网格用 pcolormesh(推荐)或 pcolor,它们接受形状为 (n+1,) 或 (n, n+1)/(n+1, n) 的 x/y 边数组。shading='auto' 根据形状选 'flat' 或 'gouraud';'nearest'/'flat' 着色单元,'gouraud' 插值。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(1)
small = rng.random((5, 5))

methods = ["none", "bilinear", "bicubic", "nearest"]
fig, axes = plt.subplots(1, 4, figsize=(12, 3))
for ax, m in zip(axes, methods):
    ax.imshow(small, interpolation=m, cmap="magma")
    ax.set_title(m)
    ax.axis("off")
plt.tight_layout()
plt.show()

加载并显示图像

mpimg.imread 把 PNG/JPG 读入 numpy 数组(float 0-1 或 uint8)。RGB 图像直接 imshow(img) 即可——不要传 cmap。axis('off') 隐藏刻度和边框,得到干净的照片展示。缩略图网格可在 subplots 中循环调用 imshow。

matplotlib
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LogNorm, TwoSlopeNorm

rng = np.random.default_rng(2)
Z = rng.random((50, 50)) * 1000 + 1

# clamp the colormap range
plt.imshow(Z, vmin=100, vmax=500, cmap="viridis")
plt.colorbar()
plt.title("clipped range")
plt.show()

# log-scaled colors
plt.imshow(Z, norm=LogNorm(vmin=1, vmax=1001), cmap="plasma")
plt.colorbar()
plt.title("log scale")
plt.show()

# diverging norm centered on a midpoint
# norm = TwoSlopeNorm(vmin=-1, vcenter=0, vmax=1)

带颜色条刻度的图像

向 colorbar 传 ticks= 设置显式刻度位置,然后用 set_yticklabels 设置自定义文字标签。imshow 返回的可映射对象(mappable)是 colorbar 读取以确定颜色映射和值范围的对象;imshow 上的 vmin/vmax 固定尺度(适合跨多个图比较)。

matplotlib
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

# read an image file into a numpy array
img = mpimg.imread("photo.png")     # shape (H, W, 3) or (H, W, 4)
print(img.shape, img.dtype)

plt.imshow(img)
plt.axis("off")
plt.show()

# save the (possibly modified) array back to a file
# plt.imsave("out.png", img)

# drop the alpha channel if present
if img.shape[-1] == 4:
    img = img[..., :3]
16

极坐标图

极坐标轴设置

传入 projection='polar'(或用 plt.polar)创建极坐标轴,其中 x 是弧度角度、y 是半径。set_theta_zero_location('N') 把 0 放在顶部;set_theta_direction(-1) 使 theta 顺时针。set_rlim 控制半径范围。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

# create a polar axes via projection
fig = plt.figure()
ax = fig.add_subplot(projection="polar")

# or from subplots
# fig, ax = plt.subplots(subplot_kw={"projection": "polar"})

theta = np.linspace(0, 2*np.pi, 100)
ax.plot(theta, np.sin(2*theta))
plt.show()

极坐标散点与柱

在极坐标轴中,ax.bar 的 width 是角度宽度(弧度),bottom 是内半径——非常适合玫瑰图。ax.scatter 用循环颜色映射(如 hsv/twilight)按角度着色,使颜色在 2π 处平滑环绕。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

theta = np.linspace(0, 2*np.pi, 200)

fig, ax = plt.subplots(subplot_kw={"projection": "polar"})
ax.plot(theta, 1 + np.cos(theta), label="cardioid")
ax.plot(theta, np.abs(np.sin(2*theta)), label="rose")
ax.set_rmax(2.0)
ax.set_rticks([0.5, 1.0, 1.5])
ax.set_thetagrids([0, 90, 180, 270])
ax.legend(loc="upper right")
plt.show()

雷达图 / 蜘蛛图

雷达图是带分类角度标签的极坐标图。要闭合多边形,把第一个值/角度附加到数组末尾。set_xticks 把类别标签放在固定角度;set_ylim 控制径向尺度。常用于多维度能力对比(如产品评分)。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

labels = ["Speed", "Power", "Range", "Comfort", "Price"]
values = [8, 7, 6, 5, 9]
N = len(labels)

# close the loop
angles = np.linspace(0, 2*np.pi, N, endpoint=False).tolist()
values += values[:1]
angles += angles[:1]

fig, ax = plt.subplots(subplot_kw={"projection": "polar"})
ax.plot(angles, values, "o-")
ax.fill(angles, values, alpha=0.25)
ax.set_xticks(angles[:-1])
ax.set_xticklabels(labels)
ax.set_title("Radar Chart")
plt.show()

极坐标网格自定义

set_theta_zero_location('N'/'E'/'S'/'W') 设置 0 弧度指向;set_theta_direction(-1) 反转方向(顺时针,如罗盘)。set_rgrids 控制径向刻度位置/标签及其角度位置;set_thetagrids 设置角度刻度。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(0)
N = 12
theta = np.linspace(0, 2*np.pi, N, endpoint=False)
widths = 2*np.pi / N
radii = rng.uniform(1, 5, N)

fig, ax = plt.subplots(subplot_kw={"projection": "polar"})
bars = ax.bar(theta, radii, width=widths, bottom=0,
              edgecolor="black")

# color bars by radius
for r, bar in zip(radii, bars):
    bar.set_facecolor(plt.cm.viridis(r / radii.max()))
ax.set_title("Rose Diagram")
plt.show()

极坐标等高线

当 X 是 theta(弧度)、Y 是 r 时,contourf 在极坐标轴中工作。网格必须是 2-D;meshgrid(theta, r) 是自然选择。适合可视化角度对称场(如天线辐射方向图)。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots(subplot_kw={"projection": "polar"})
ax.plot(np.linspace(0, 2*np.pi, 100), np.linspace(0, 1, 100))

# rotate 0 degrees to the top, go clockwise
ax.set_theta_zero_location("N")
ax.set_theta_direction(-1)

# radial limits and ticks
ax.set_rlim(0, 1.2)
ax.set_rticks([0.25, 0.5, 0.75, 1.0])
ax.set_rlabel_position(135)   # move radial labels out of the way

# label directions in degrees
ax.set_thetagrids([0, 90, 180, 270], labels=["E","N","W","S"])
plt.show()
17

双轴与次级刻度

twinx(双 y 轴)

ax.twinx() 创建共享 x 轴、在右侧有独立 y 轴的新坐标轴。把每条线的标签/刻度颜色匹配,让读者知道哪个轴属于哪条系列。twiny() 对 x 轴做同样的事(两个 x 轴,共享 y)。常用于在同一图上比较量级差异大的两个系列(如温度与销量)。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)

fig, ax1 = plt.subplots()
ax1.plot(x, np.sin(x), "b-")
ax1.set_xlabel("x")
ax1.set_ylabel("sin(x)", color="b")
ax1.tick_params(axis="y", labelcolor="b")

# a second axes sharing the same x, with an independent y
ax2 = ax1.twinx()
ax2.plot(x, x*10, "r-")
ax2.set_ylabel("10*x", color="r")
ax2.tick_params(axis="y", labelcolor="r")

plt.title("twinx: two y-scales")
plt.show()

twiny(双 x 轴)

ax.twiny() 在坐标轴顶部创建第二个 x 轴(共享 y)。适合在同一数据上显示两个尺度(如秒与频率、摄氏与华氏)。在 twin 上手动 set_xticks/labels 显示次级尺度。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

y = np.linspace(0, 10, 50)

fig, ax1 = plt.subplots()
ax1.plot(np.exp(y), y, "g-")
ax1.set_ylabel("y")
ax1.set_xlabel("exp(y)", color="g")

# second axes sharing y, independent x on top
ax2 = ax1.twiny()
ax2.plot(y**2, y, "m-")
ax2.set_xlabel("y^2", color="m")

plt.title("twiny: two x-scales")
plt.show()

次级坐标轴变换

secondary_xaxis('top', functions=(f, g)) 添加一个 x 轴,其刻度由对主轴值应用 f 计算,g 作为逆函数。secondary_yaxis('right', ...) 对 y 做同样的事。适合单位转换(摄氏转华氏、米转千米等),无需手动设置两套刻度。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

t = np.linspace(0, 5, 100)
temp_c = 20 + 5*np.sin(t)         # Celsius
humidity = 60 - 5*t               # percent

fig, ax1 = plt.subplots(figsize=(8, 4))

l1, = ax1.plot(t, temp_c, "r-o", label="Temperature (C)")
ax1.set_xlabel("time (s)")
ax1.set_ylabel("Temperature (C)", color="r")
ax1.tick_params(axis="y", labelcolor="r")

ax2 = ax1.twinx()
l2, = ax2.plot(t, humidity, "b-s", label="Humidity (%)")
ax2.set_ylabel("Humidity (%)", color="b")
ax2.tick_params(axis="y", labelcolor="b")

# combine legends from both axes
lines = [l1, l2]
ax1.legend(lines, [l.get_label() for l in lines], loc="upper right")
plt.title("Temperature & Humidity")
plt.show()

断轴

当数据跨越大不相同的范围时,用两个堆叠子图(sharex=True)和紧凑的 ylim 值模拟'断轴'。隐藏内部边框,用对角标记(ax.plot 配 transform)表示断裂。这避免了用对数轴扭曲数据,同时仍能紧凑显示两个尺度。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()

ax1.plot(x, np.sin(x), color="tab:blue")
ax2.plot(x, np.cos(x), color="tab:orange")

# remove the top spine so the two axes don't double-border
ax1.spines["top"].set_visible(False)
ax2.spines["top"].set_visible(False)

# color the spines to match their axes
ax1.spines["left"].set_color("tab:blue")
ax2.spines["right"].set_color("tab:orange")

plt.title("Clean Twin Axes")
plt.show()

子图间共享坐标轴

sharex/sharey='all'(或 True)链接子图间的缩放/平移,使它们保持同步。用 'col' 或 'row' 仅在列/行内共享。内部坐标轴的刻度标签会自动隐藏以减少杂乱。在比较多个数据集时至关重要,否则不同子图可能显示误导性的不同尺度。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

km = np.linspace(0, 100, 50)
mile = km * 0.621371

fig, ax1 = plt.subplots()
ax1.plot(km, np.sin(km/10), "tab:purple")
ax1.set_xlabel("distance")
ax1.set_ylabel("signal", color="tab:purple")

ax2 = ax1.twiny()
ax2.set_xlim(ax1.get_xlim())          # align x limits
# relabel the top ticks in miles
new_ticks = np.array([0, 25, 50, 75, 100])
ax2.set_xticks(new_ticks)
ax2.set_xticklabels([f"{m:.0f}" for m in new_ticks*0.621371])
ax2.set_xlabel("miles")
plt.show()
18

误差棒与填充区域

基础误差棒

errorbar 绘制带垂直(及水平,用 xerr)误差棒的点。fmt 是绘图格式字符串('o'、's-' 等)。capsize 在棒端添加小水平端帽;ecolor 设置误差棒颜色,独立于线条颜色。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 5, 10)
y = np.exp(-x)
yerr = 0.1 + 0.1*x      # error grows with x

plt.errorbar(x, y, yerr=yerr, fmt="o-", color="tab:blue",
             ecolor="gray", elinewidth=1.5, capsize=4,
             markersize=6, label="exp(-x) +/- err")
plt.legend()
plt.title("Error Bar Plot")
plt.show()

非对称误差

向 yerr(或 xerr)传入 2×N 数组指定非对称误差:第 0 行是下界,第 1 行是上界。对称误差传 1-D 数组或标量。配合 fmt='o'(无线)得到干净的误差棒散点。常用于实验数据上下界不对称的情况。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.arange(5)
y = np.array([2, 3, 5, 4, 6])
# separate lower and upper errors (each a 1-D array)
yerr = [[0.5, 1, 0.8, 0.6, 1.2],   # lower
        [1.0, 0.8, 1.5, 1.0, 0.7]]  # upper

plt.errorbar(x, y, yerr=yerr, fmt="s", color="tab:green",
             capsize=5)
plt.title("Asymmetric Error Bars")
plt.show()

# horizontal asymmetric errors work the same way
# xerr = [[...lower...], [...upper...]]

fill_between

fill_between 在曲线和基线(默认 0)之间填充。where= 接受布尔数组,只填充部分——适合突出曲线正/负区域。interpolate=True 避免曲线交叉处的间隙。常用于强调某条件下的数据范围。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y = np.sin(x)
std = 0.2

plt.plot(x, y, "b-", label="mean")
# shade +/- one standard deviation
plt.fill_between(x, y - std, y + std, color="blue", alpha=0.2,
                 label="+/- 1 std")

# shade only where a condition holds
plt.fill_between(x, y, 0, where=(y > 0), color="green", alpha=0.2,
                 label="y > 0")
plt.legend()
plt.show()

fill_between 置信带

fill_between(x, y_lower, y_upper) 在两条曲线之间填充——这是围绕均值曲线绘制置信带的标准做法。配合 plot(x, y_mean) 画中心线。alpha 控制带透明度。在机器学习预测区间、统计置信区间可视化中常用。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

years = np.arange(2010, 2021)
a = np.array([10, 12, 14, 15, 17, 18, 20, 22, 23, 25, 27])
b = np.array([ 5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15])
c = np.array([ 3,  4,  4,  5,  6,  6,  7,  8,  8,  9, 10])

plt.stackplot(years, a, b, c, labels=["A", "B", "C"],
              colors=["#4C72B0", "#55A868", "#C44E52"], alpha=0.8)
plt.legend(loc="upper left")
plt.title("Stacked Area Chart")
plt.xlabel("year")
plt.ylabel("value")
plt.show()

stackplot(堆叠面积)

stackplot 把多个 y 系列堆叠在一起,适合展示随时间变化的组成。baseline='zero'(默认)、'sym'(围绕 0 对称)、'wiggle'(最小化加权变化)、'weighted_wiggle'。传入 colors 和 labels 列表。

matplotlib
import matplotlib.pyplot as plt
import numpy as np

# fill a closed polygon defined by its vertices
x = [0, 1, 1, 0, 0]
y = [0, 0, 1, 1, 0]
plt.fill(x, y, color="tab:cyan", alpha=0.5)

# shade under a curve
xs = np.linspace(0, 2*np.pi, 100)
ys = np.sin(xs)
plt.fill(np.r_[xs, xs[::-1]],
         np.r_[ys, np.zeros_like(ys)],
         color="tab:orange", alpha=0.3)

plt.title("Filled Polygons")
plt.show()
19

Seaborn 集成

设置主题与样式

sns.set_theme 全局配置 matplotlib:style(darkgrid/whitegrid/dark/white/ticks)、context(paper/notebook/talk/poster——缩放字体/元素)、palette(deep/muted/pastel/bright/dark/colorblind)。用 axes_style() 在 with 块内做局部覆盖。

matplotlib
# install seaborn
pip install seaborn

import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

# Seaborn is built ON matplotlib: any sns plot returns an Axes
# and accepts an ax= argument, so you can mix freely.

sns.set_theme()        # apply seaborn's default style
tips = sns.load_dataset("tips")

ax = sns.scatterplot(data=tips, x="total_bill", y="tip", hue="time")
ax.set_title("Tips dataset")
plt.show()

统计图

sns.violinplot 按类别显示完整分布(KDE);split=True 把 hue 等级放在小提琴两侧。inner='quartile'/'box'/'point' 控制内部标记。boxplot 是经典五数概括;stripplot/swarmplot 叠加单个点。Seaborn 内置统计聚合让复杂可视化变得简单。

matplotlib
import seaborn as sns
import matplotlib.pyplot as plt

# five built-in themes: darkgrid, whitegrid, dark, white, ticks
sns.set_style("whitegrid")
sns.set_context("notebook")   # paper, notebook, talk, poster

# set the color palette
sns.set_palette("Set2")

import numpy as np
for i in range(4):
    plt.plot(np.arange(10), np.arange(10)+i)
plt.title("Seaborn-styled")
plt.show()

# remove the top/right spines (despine) for a clean look
# sns.despine()

回归图与联合图

regplot 拟合并绘制带 95% 置信区间的线性回归;scatter_kws/line_kws 传递到底层绘图调用。jointplot 创建独立图形,中央散点/六边形/KDE 加边缘直方图——kind='scatter'/'hex'/'kde'/'reg'/'hist'/'resid'。是探索两个变量关系的利器。

matplotlib
import seaborn as sns
import matplotlib.pyplot as plt

tips = sns.load_dataset("tips")

# scatter or line, faceted by a categorical variable
g = sns.relplot(data=tips, x="total_bill", y="tip",
                hue="smoker", style="time", col="day",
                kind="scatter", height=3, aspect=1.2)

g.fig.suptitle("Tips by day", y=1.03)
plt.show()

FacetGrid(小倍数图)

FacetGrid 创建按分类列分割的子图网格——这是跨组比较分布的最清晰方式。map_dataframe 把 seaborn/plt 绘图函数应用到每个子集。用 col_wrap 把列换行到多行。set_axis_labels/set_titles 自定义标签和标题模板。

matplotlib
import seaborn as sns
import matplotlib.pyplot as plt

tips = sns.load_dataset("tips")

# histogram with an overlaid KDE (density curve)
sns.histplot(data=tips, x="total_bill", bins=20, kde=True,
             hue="time", stat="density", common_norm=False)
plt.title("Histogram + KDE")
plt.show()

# standalone KDE
plt.figure()
sns.kdeplot(data=tips, x="total_bill", hue="time", fill=True)
plt.show()

pairplot 与 heatmap

pairplot 把每个数值列与其他列两两绘制,按分类 hue 着色——完美用于发现聚类。heatmap 绘制二维矩阵,颜色表示值;annot=True 显示数值,vmin/vmax 固定尺度,square=True 保持单元方形。correlation heatmap 是数据探索的常见起点。

matplotlib
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

# correlation matrix
tips = sns.load_dataset("tips")
num = tips.select_dtypes("number").drop(columns=["size"])
corr = num.corr()

sns.heatmap(corr, annot=True, cmap="coolwarm", center=0,
            vmin=-1, vmax=1, square=True,
            linewidths=0.5, fmt=".2f")
plt.title("Correlation Heatmap")
plt.show()

调色板

color_palette('name', n) 返回 RGB 元组列表;as_cmap=True 返回 matplotlib Colormap。顺序(flare、viridis)、发散(vlag、coolwarm)、定性(Set2、deep)——按数据类型选择。palplot 把调色板显示为水平条带,便于预览。

matplotlib
import seaborn as sns
import matplotlib.pyplot as plt

iris = sns.load_dataset("iris")

# scatter matrix: every numeric column vs every other
g = sns.pairplot(iris, hue="species", height=2.5)
g.fig.suptitle("Iris pairplot", y=1.01)
plt.show()

# violin plot: box + kernel density on each side
plt.figure()
sns.violinplot(data=iris, x="species", y="sepal_length",
               inner="quartile")
plt.show()
20

交互式控件与动画

FuncAnimation 基础

FuncAnimation 每 interval 毫秒调用 update(frame)。update 必须返回已更改艺术家的可迭代对象(blit=True 时高效地只重绘它们)。用 ani.save('out.gif'/'out.mp4', writer='pillow'/'ffmpeg') 保存。frames 可以是 range、列表或生成器。

matplotlib
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
import numpy as np

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

x = np.linspace(0, 10, 500)
line, = ax.plot(x, np.sin(x))

# axes for the slider
ax_freq = plt.axes([0.2, 0.1, 0.6, 0.03])
slider = Slider(ax_freq, "freq", 0.1, 5.0, valinit=1.0)

def update(val):
    line.set_ydata(np.sin(slider.val * x))
    fig.canvas.draw_idle()

slider.on_changed(update)
plt.show()

滑块与按钮

用 plt.axes([left, bottom, width, height]) 在图形分数坐标中手动放置 Slider/Button 坐标轴。on_changed(callback) 在滑块移动时触发;调用 fig.canvas.draw_idle() 刷新。Button 用 on_clicked(callback)。总是 subplots_adjust 为控件留出空间。

matplotlib
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
import numpy as np

fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.2)
(ln,) = ax.plot(np.random.rand(10))

ax_btn = plt.axes([0.7, 0.05, 0.2, 0.075])
btn = Button(ax_btn, "Resample")

def reshuffle(event):
    ln.set_ydata(np.random.rand(10))
    fig.canvas.draw_idle()

btn.on_clicked(reshuffle)
plt.show()

鼠标事件与拾取

mpl_connect 接入 GUI 事件循环。常见事件:'button_press_event'、'motion_notify_event'、'key_press_event'、'pick_event'。在艺术家上设 picker=5(点容差)启用拾取事件;event.ind 给出被拾取的索引。用于实现自定义交互(点击高亮、悬停信息)。

matplotlib
import matplotlib.pyplot as plt
from matplotlib.widgets import CheckButtons
import numpy as np

x = np.linspace(0, 5, 200)
l1, = plt.plot(x, np.sin(x), label="sin")
l2, = plt.plot(x, np.cos(x), label="cos")
plt.subplots_adjust(right=0.8)

rax = plt.axes([0.82, 0.4, 0.15, 0.2])
check = CheckButtons(rax, ["sin", "cos"], [True, True])

def toggle(label):
    if label == "sin": l1.set_visible(not l1.get_visible())
    if label == "cos": l2.set_visible(not l2.get_visible())
    plt.draw()

check.on_clicked(toggle)
plt.show()

光标标注

mplcursors 为任何艺术家添加交互式悬停/点击标注。cursor(sc) 附加到特定艺术家;hover=True 鼠标悬停时显示工具提示。用 cursors.cursor(sc).connect('add', lambda sel: sel.annotation.set_text(...)) 自定义格式。

matplotlib
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4], [1, 4, 2, 3])

def on_click(event):
    if event.inaxes != ax:
        return
    print(f"clicked at x={event.xdata:.2f} y={event.ydata:.2f}")

def on_move(event):
    if event.inaxes == ax:
        ax.set_title(f"x={event.xdata:.2f}, y={event.ydata:.2f}")
        fig.canvas.draw_idle()

fig.canvas.mpl_connect("button_press_event", on_click)
fig.canvas.mpl_connect("motion_notify_event", on_move)
plt.show()

Notebook 交互性

%matplotlib widget(需 ipympl)在 JupyterLab 中启用内联交互式图形。配合 ipywidgets.interact 用滑块/下拉框重新运行函数——函数修改艺术家并调用 draw_idle() 刷新,而无需重建图形。是数据探索的强大组合。

matplotlib
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np

fig, ax = plt.subplots()
x = np.linspace(0, 2*np.pi, 200)
line, = ax.plot(x, np.sin(x))

def update(frame):
    line.set_ydata(np.sin(x + frame / 10.0))
    return line,

ani = animation.FuncAnimation(fig, update, frames=100,
                              interval=50, blit=True, repeat=True)
# save to a file (requires ffmpeg or pillow installed)
# ani.save("wave.mp4", writer="ffmpeg")
# ani.save("wave.gif", writer="pillow")
plt.show()

这篇内容对您有帮助吗?

学习路径

从零开始学习

通过结构化课程从头学习这个语言。