← 返回Book 2目录
第03章

布局

Layout Design · 图表布局与子图系统

掌握Matplotlib布局系统:subplots、GridSpec与嵌套子图

📚 章节概述

数据可视化中,布局设计至关重要。本章深入讲解Matplotlib中的多种布局方案, 从基础的subplots到灵活的GridSpec,再到复杂的嵌套子图系统,帮助你创建专业级的复合图表。

📐 基础布局
subplots规则网格布局
🎯 灵活布局
GridSpec自定义子图大小
🔄 嵌套布局
subgridspec多层次结构

3.1 图片对象规格

plt.rcParams - Matplotlib全局参数

plt.rcParams 是一个字典对象, 包含了Matplotlib所有可配置的参数。通过修改这些参数,可以定制图表的各个方面。

主要参数类别:

axes.*
坐标轴属性:颜色、线宽、标签大小等
figure.*
图片整体属性:大小、DPI、背景色
font.*
字体属性:family、size、weight
grid.*
网格属性:颜色、线型、透明度
legend.*
图例属性:位置、字体、边框
lines.*
线条属性:线宽、线型、标记
import matplotlib.pyplot as plt
import numpy as np

# 查看所有默认参数
plt.rcParams  

# 常用参数修改示例
plt.rcParams['figure.figsize'] = (10, 6)  # 图片大小
plt.rcParams['font.size'] = 12             # 字体大小
plt.rcParams['axes.grid'] = True           # 显示网格
plt.rcParams['grid.alpha'] = 0.3           # 网格透明度

💡 最佳实践:在大型项目中,建议创建统一的样式配置文件(.mplstyle),保持整个项目图表风格一致。

🏔

MATLAB Peaks 函数探索器

经典的三维测试函数,展示多峰地形

30 × 30
粗糙 (10)精细 (50)
-3-3-2-2-1-100112233xyMATLAB Peaks Function (2D View)

📐 函数定义

z = 3(1-x)² exp(-(x²)-(y+1)²) - 10(x/5-x³-y⁵) exp(-x²-y²) - (1/3) exp(-(x+1)²-y²)
🎯 函数特点
  • • 三个高斯型峰的线性组合
  • • 定义域:x, y ∈ [-3, 3]
  • • 包含多个局部极值点
  • • 经典的优化测试函数
💡 应用场景
  • • 测试3D绘图能力
  • • 演示曲面可视化
  • • 优化算法验证
  • • 多峰函数分析
🌈 颜色映射
低值 (蓝色)中值 (黄色)高值 (红色)

3.2 混合二维、三维可视化方案

MATLAB Peaks函数可视化

Peaks函数是MATLAB中经典的测试函数,用于展示二元函数的可视化效果。 本节展示如何使用Python重现该函数,并进行多种形式的可视化。

from sympy import symbols, exp, lambdify
import numpy as np

x, y = symbols("x, y")

# 定义MATLAB二元函数 peaks()
f_xy = 3*(1-x)**2*exp(-(x**2) - (y+1)**2) \
    - 10*(x/5 - x**3 - y**5)*exp(-x**2-y**2) \
    - 1/3*exp(-(x+1)**2 - y**2)

# 转换为Python函数
f_xy_fcn = lambdify([x,y], f_xy)

# 创建网格
x_vals = np.linspace(-3, 3, 100)
y_vals = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x_vals, y_vals)
Z = f_xy_fcn(X, Y)
二维可视化方法
  • 等高线图(contour)
  • 填充等高线(contourf)
  • 热力图(imshow)
  • 矢量场(quiver)
三维可视化方法
  • 曲面图(plot_surface)
  • 线框图(plot_wireframe)
  • 三维等高线(contour3D)
  • 投影等高线

3.3 使用subplots绘制规则网格布局

subplots - 快速创建规则网格

plt.subplots() 是创建多子图最简单的方法, 适用于规则的行列布局。

from scipy.stats import norm
import matplotlib.pyplot as plt
import numpy as np

# 创建5x5的子图网格
fig, axs = plt.subplots(nrows=5, ncols=5, figsize=(10, 10))

x_array = np.linspace(-5, 5, 200)
mu_array = [-2, -1, 0, 1, 2]
sigma_array = [0.5, 0.75, 1, 1.5, 2]

# 使用meshgrid生成参数组合
mu_array_, sigma_array_ = np.meshgrid(mu_array, sigma_array)

# 遍历每个子图
for mu_idx, sigma_idx, ax in zip(mu_array_.ravel(), 
                                   sigma_array_.ravel(), 
                                   axs.ravel()):
    # 计算一元高斯分布PDF
    pdf_vals = norm.pdf(x_array, loc=mu_idx, scale=sigma_idx)
    
    # 绘制曲线
    ax.plot(x_array, pdf_vals, 'b-', linewidth=1.5)
    ax.fill_between(x_array, pdf_vals, alpha=0.2)
    
    # 设置标题
    ax.set_title(f'μ={mu_idx}, σ={sigma_idx}', fontsize=8)
    ax.set_xlim(-5, 5)
    ax.set_ylim(0, None)
    
plt.tight_layout()
plt.show()

📊 一元高斯分布参数效果

均值 μ 的作用:
控制分布的中心位置,曲线沿x轴平移
标准差 σ 的作用:
控制分布的宽度,σ越大曲线越平缓分散
𝒩

一元高斯分布探索器

调节均值μ和标准差σ,观察分布形态变化

-303
0.31.02.0
-5.0-4.0-3.0-2.0-1.00.01.02.03.04.05.0xPDFN(μ=0.0, σ²=1.00)

📊 参数效果说明

均值 μ(红色虚线)
控制分布的中心位置,曲线整体左右平移。μ增大时,分布右移;μ减小时,分布左移。
标准差 σ(绿色虚线)
控制分布的离散程度。σ越大,曲线越平坦分散;σ越小,曲线越陡峭集中。
💡 概率密度函数 (PDF)
f(x) = (1 / (σ√(2π))) × exp(-(x-μ)² / (2σ²))

Beta分布可视化

Beta分布是定义在[0,1]区间上的连续概率分布,由两个形状参数α和β控制。

from scipy.stats import beta

x_array = np.linspace(0, 1, 200)
alpha_array = [0.5, 1, 2, 4, 8]
beta_array = [0.5, 1, 2, 4, 8]

fig, axs = plt.subplots(nrows=5, ncols=5, figsize=(10, 10))

alpha_array_, beta_array_ = np.meshgrid(alpha_array, beta_array)

for alpha_idx, beta_idx, ax in zip(alpha_array_.ravel(), 
                                     beta_array_.ravel(), 
                                     axs.ravel()):
    pdf_vals = beta.pdf(x_array, alpha_idx, beta_idx)
    ax.plot(x_array, pdf_vals, 'r-', linewidth=1.5)
    ax.fill_between(x_array, pdf_vals, alpha=0.2, color='red')
    ax.set_title(f'α={alpha_idx}, β={beta_idx}', fontsize=8)
    ax.set_xlim(0, 1)
    
plt.tight_layout()

📊 Beta分布形状特点

  • α < 1, β < 1:U形分布,两端概率密度大
  • α = β = 1:均匀分布
  • α > 1, β > 1:单峰分布,峰值位置取决于α和β的相对大小
  • α = β:关于0.5对称
β

Beta分布探索器

调节形状参数α和β,观察分布形态变化

0.3510
0.3510
快速预设
当前形态:左偏分布
0.000.250.500.751.00xPDFBeta(α=2.0, β=5.0)

📊 参数效果说明

均值 (红色虚线)
μ = α / (α + β) = 0.286
众数 (绿色虚线)
mode = (α-1) / (α+β-2) = 0.200
💡 参数α的作用
  • • α增大:分布右移,更集中于大值
  • • α < 1:左端趋近无穷
  • • α = 1:左端有限值
  • • α > 1:左端为零
💡 参数β的作用
  • • β增大:分布左移,更集中于小值
  • • β < 1:右端趋近无穷
  • • β = 1:右端有限值
  • • β > 1:右端为零
🔢 概率密度函数 (PDF)
f(x; α, β) = [x^(α-1) × (1-x)^(β-1)] / B(α, β), x ∈ [0,1]

3.4 使用GridSpec创建灵活布局

GridSpec - 自定义子图大小和位置

GridSpec提供了比subplots更灵活的布局控制,可以创建不同大小的子图, 实现主图与边缘分布图的组合布局。

import matplotlib.pyplot as plt
import numpy as np

# 生成二元高斯分布随机数
mean = [0, 0]
cov = [[1, 1], [1, 2]]
x, y = np.random.multivariate_normal(mean, cov, 1000).T

# 创建4x4的GridSpec
fig = plt.figure(figsize=(6, 6))
grid = plt.GridSpec(4, 4, hspace=0.3, wspace=0.3)

# 主图占据右上3x3区域
main_ax = fig.add_subplot(grid[:-1, 1:])
# x轴边缘直方图占据底部1x3区域
x_hist = fig.add_subplot(grid[-1, 1:], yticklabels=[])
# y轴边缘直方图占据左侧3x1区域
y_hist = fig.add_subplot(grid[:-1, 0], xticklabels=[])

# 绘制散点图
main_ax.plot(x, y, 'ob', markersize=3, alpha=0.2)
main_ax.set_xticks([])
main_ax.set_yticks([])

# 绘制x边缘直方图
x_hist.hist(x, 40, orientation='vertical', color='b',
            edgecolor='white', linewidth=0.5)
x_hist.invert_yaxis()  # 反转y轴,让直方图向下
x_hist.set_xticks([])
x_hist.set_yticks([])

# 绘制y边缘直方图
y_hist.hist(y, 40, orientation='horizontal', color='b',
            edgecolor='white', linewidth=0.5)
y_hist.invert_xaxis()  # 反转x轴,让直方图向左
y_hist.set_xticks([])
y_hist.set_yticks([])

plt.show()

🎯 主图位于右上

grid[:-1, 1:]
主图占据除最后一行和第一列外的区域

🎯 主图位于左下

grid[1:, :-1]
主图占据除第一行和最后一列外的区域

💡 设计要点:边缘分布图展示了二维分布在各个维度上的投影,帮助理解数据的单变量特征。 这种布局在探索性数据分析中非常常用。

⊞

GridSpec 布局演示

灵活的子图网格系统,类似matplotlib的GridSpec

主图 (2×2)
colspan=2, rowspan=2
侧边图
rowspan=2
子图1
子图2
子图3

📐 当前布局特性

布局信息
基础网格布局
3×3网格,展示不同的跨行跨列组合
主要特性
  • • 主图区域 (2×2)
  • • 侧边栏 (1×2)
  • • 底部子图 (1×1 × 3)
💡 GridSpec 优势
  • • 灵活的子图排列
  • • 支持跨行跨列
  • • 可以嵌套使用
  • • 精确控制间距
🎯 使用场景
  • • 复杂的多子图布局
  • • 仪表板设计
  • • 数据报告可视化
  • • 对比分析图表
📝 实现方式

本演示使用CSS Grid实现类似matplotlib GridSpec的功能:

  • • grid-cols-* - 定义列数
  • • grid-rows-* - 定义行数
  • • col-span-* - 跨列
  • • row-span-* - 跨行

3.5 Dirichlet分布与边缘Beta分布可视化

联合分布与边缘分布的GridSpec布局

Dirichlet分布是Beta分布在多维空间的推广。本节展示如何使用GridSpec 同时绘制Dirichlet联合分布的等高线和边缘Beta分布。

数学关系:

Dirichlet分布:
Dir(α₁, α₂, α₃), 其中 x₁ + x₂ + x₃ = 1
边缘分布:
x₁ ~ Beta(α₁, α₂ + α₃)
x₂ ~ Beta(α₂, α₁ + α₃)
import scipy.stats as stats
import matplotlib.gridspec as gridspec

# Dirichlet参数
alpha_1, alpha_2, alpha_3 = 2, 2, 2
alpha_0 = alpha_1 + alpha_2 + alpha_3

# 创建网格
x1 = np.linspace(0, 1, 201)
x2 = np.linspace(0, 1, 201)
xx1, xx2 = np.meshgrid(x1, x2)
xx3 = 1.0 - xx1 - xx2
xx3 = np.where(xx3 > 0.0, xx3, np.nan)

# 计算Dirichlet PDF
alphas = np.array([alpha_1, alpha_2, alpha_3])
rv = stats.dirichlet(alphas)
PDF_ff = rv.pdf(np.array([xx1.ravel(), xx2.ravel(), xx3.ravel()]))
PDF_ff = np.reshape(PDF_ff, xx1.shape)

# 计算边缘Beta分布
beta_1 = alpha_0 - alpha_1
f_x1 = stats.beta.pdf(x1, alpha_1, beta_1)

beta_2 = alpha_0 - alpha_2
f_x2 = stats.beta.pdf(x2, alpha_2, beta_2)

# 创建2x2 GridSpec布局
fig = plt.figure(figsize=(6, 6))
gs = gridspec.GridSpec(2, 2, 
                       width_ratios=[3, 1], 
                       height_ratios=[3, 1])

# 左上:联合分布等高线
ax1 = plt.subplot(gs[0])
ax1.contour(xx1, xx2, PDF_ff, 20, cmap='RdYlBu_r')
ax1.set_title(f'Dirichlet({alpha_1}, {alpha_2}, {alpha_3})')

# 右上:x₂边缘分布
ax2 = plt.subplot(gs[1])
ax2.plot(f_x2, x2, color='#0070C0')
ax2.fill_between(f_x2, x2, alpha=0.2)
ax2.set_xlabel('PDF')
ax2.invert_xaxis()

# 左下:x₁边缘分布
ax3 = plt.subplot(gs[2])
ax3.plot(x1, f_x1, color='#0070C0')
ax3.fill_between(x1, f_x1, alpha=0.2)
ax3.set_ylabel('PDF')

# 右下:隐藏
ax4 = plt.subplot(gs[3])
ax4.set_visible(False)

💡 布局特点:使用width_ratios和height_ratios参数, 可以精确控制主图与边缘图的尺寸比例,通常设为3:1或4:1效果最佳。

△

Dirichlet 分布探索器

多元概率分布的三元图可视化

参数设置

2
2
2
100
α₃α₁α₂Dirichlet Distribution Ternary Plot网格线表示等值线

📊 分布参数信息

分量 1
α₁ = 2
期望: 0.333
分量 2
α₂ = 2
期望: 0.333
分量 3
α₃ = 2
期望: 0.333
🎯 Dirichlet 分布
  • • Beta分布的多元推广
  • • 输出K个概率值,和为1
  • • 用于表示概率分布的分布
  • • α参数控制集中程度
💡 应用场景
  • • 主题模型 (LDA)
  • • 贝叶斯推断的先验
  • • 多分类问题
  • • 文档分类与聚类
📐 三元图说明

• 三角形的每个顶点代表一个分量取值为1的情况

• 点在三角形内的位置表示三个分量的相对大小

• 点的颜色使用RGB映射,对应三个分量的值

• α值越大,样本越集中在对应顶点附近

3.6 使用subgridspec创建嵌套子图

多层次嵌套布局

subgridspec 允许在GridSpec的每个单元格内 再创建一个子GridSpec,实现复杂的多层次布局结构。

import matplotlib.pyplot as plt
import numpy as np

def squiggle_xy(a, b, c, d, i=np.arange(0.0, 2*np.pi, 0.05)):
    """生成参数化曲线"""
    return np.sin(i*a)*np.cos(i*b), np.sin(i*c)*np.cos(i*d)

fig = plt.figure(figsize=(8, 8), constrained_layout=False)

# 创建4x4的外层网格
outer_grid = fig.add_gridspec(4, 4, wspace=0, hspace=0)

# 在每个外层单元格中创建3x3的内层网格
for a in range(4):
    for b in range(4):
        # 嵌套GridSpec
        inner_grid = outer_grid[a, b].subgridspec(3, 3, 
                                                   wspace=0, hspace=0)
        axs = inner_grid.subplots()  # 创建内层所有子图
        
        # 在每个内层子图中绘制参数曲线
        for (c, d), ax in np.ndenumerate(axs):
            ax.plot(*squiggle_xy(a + 1, b + 1, c + 1, d + 1))
            ax.set(xticks=[], yticks=[])

# 只显示最外层边框
for ax in fig.get_axes():
    ss = ax.get_subplotspec()
    ax.spines.top.set_visible(ss.is_first_row())
    ax.spines.bottom.set_visible(ss.is_last_row())
    ax.spines.left.set_visible(ss.is_first_col())
    ax.spines.right.set_visible(ss.is_last_col())

plt.show()

📊 布局层次

  • 外层:4×4 = 16个大格子
  • 内层:每个大格子内3×3 = 9个小格子
  • 总计:16×9 = 144个子图

🎨 应用场景

  • 参数空间扫描
  • 多层次数据对比
  • 复杂模式展示
  • 科学研究报告

⚠️ 注意事项:嵌套子图布局功能强大但也增加了复杂度。建议在确实需要展示多层次结构时使用, 并注意控制图片尺寸和分辨率,避免单个子图过小难以辨识。

📋 本章总结

核心知识点

  • 1.plt.rcParams:全局参数配置,统一图表风格
  • 2.plt.subplots:规则网格布局,快速创建多子图
  • 3.GridSpec:灵活布局,自定义子图大小和位置
  • 4.subgridspec:嵌套布局,实现多层次结构

布局选择建议

规则网格 →使用 subplots
主图+边缘图 →使用 GridSpec
多层次结构 →使用 subgridspec

📚 参考资源