Matplotlib(五)- 绘制子图

发布于:2025-08-04 ⋅ 阅读:(10) ⋅ 点赞:(0)

文章目录


一、子图概述

在数据可视化中,当需要同时展示多组数据或从不同维度呈现同一数据时,子图(Subplot)是一种高效的解决方案。通过将多个图表合理排布在同一画布上,既能保证数据间的关联性,又能实现对比分析,提升信息传递的效率。

1. 子图介绍

子图是指在同一个画布(Figure)中创建的多个独立图表,每个子图拥有自己的坐标轴(Axes)、标题、标签等元素,可独立设置样式和数据,共享画布的整体布局空间。其核心作用包括:

  • 多数据对比:同时展示不同数据系列的趋势(如同一时间段内不同产品的销量对比);
  • 多维度分析:从不同角度呈现同一数据(如原始数据、累计数据、增长率数据的联动展示);
  • 空间高效利用:在有限画布内整合多个相关图表,避免多次绘图的繁琐。

在 Matplotlib 中,子图的创建和管理是通过 FigureAxes 对象实现的:Figure 代表整个画布,Axes 则代表每个子图的坐标轴区域,一个 Figure 可以包含多个 Axes(子图)。

2. 子图布局

子图的布局决定了多个子图在画布中的排列方式,Matplotlib 支持两种主要布局模式:网格布局(规则排列)和自由布局(灵活排布),分别适用于不同的展示需求。

2.1 网格布局

网格布局是将子图按照规则的行列网格进行排列(如 2 行 3 列、1 行 2 列等),每个子图占据网格中的一个单元格,适合展示结构规整、数量固定的子图。这种布局的优势是整齐有序,便于批量创建和管理,尤其适合对比性质的多组数据展示。

常用实现方法及特点:

实现方法 语法/示例 核心功能 适用场景 优势特点
plt.subplots() fig, axes = plt.subplots(nrows=2, ncols=2) 一次性创建指定行列数的网格子图,返回画布(fig)和子图数组(axes 行列数固定、需批量创建子图的场景 操作简洁,支持通过数组索引快速访问子图;可统一设置 sharex/sharey 共享坐标轴
plt.subplot() ax1 = plt.subplot(2, 2, 1)(2行2列网格中的第1个子图,index从1开始) 逐个创建子图,通过 rows, cols, index 指定子图在网格中的位置 按需创建单个子图,或动态生成网格布局 灵活性高,可根据需求分步创建子图;无需提前确定所有子图数量
GridSpec python<br>gs = plt.GridSpec(2, 2)<br>ax1 = plt.subplot(gs[0, :]) # 第1行占满2列<br> 自定义网格行列跨度,支持子图合并单元格(如跨多行/多列) 复杂网格布局(如部分子图需占据更大空间) 支持非均匀布局,可精确控制子图尺寸和位置;适合突出重要子图的场景
网格布局整体特点
1. 子图位置由行数(nrows)和列数(ncols)严格定义,排列整齐有序;
2. 支持通过 sharex(共享x轴)、sharey(共享y轴)减少重复坐标轴标签,提升可读性;
3. 适合子图数量固定、需要统一风格的场景(如论文中的多组实验结果对比、同一指标的多维度分析)。

2.2 自由布局

自由布局允许子图在画布中按照自定义的位置和大小进行排布,不受网格行列的限制,适合展示结构不规则或需要突出某个子图的场景。这种布局的优势是灵活性高,可根据子图的重要性调整大小和位置,突出核心信息。

常用实现方法及特点:

实现方法 核心功能 语法/示例 参数说明([left, bottom, width, height] 适用场景
plt.axes() 通过相对坐标和尺寸创建子图,直接关联当前画布 ax1 = plt.axes([0.1, 0.1, 0.4, 0.4]) - left:子图左边缘距画布左边界的相对距离(0-1)
- bottom:子图下边缘距画布下边界的相对距离(0-1)
- width:子图宽度占画布的比例(0-1)
- height:子图高度占画布的比例(0-1)
快速创建单个自由布局子图,脚本式编程场景
Figure.add_axes() 通过画布对象添加子图,需先创建画布,适合面向对象编程 python<br>fig = plt.figure()<br>ax2 = fig.add_axes([0.6, 0.1, 0.3, 0.7])<br> plt.axes() 参数完全一致,参数含义同上 多画布管理,需明确关联子图所属画布的场景
自由布局的整体特点
1. 子图位置和大小完全自定义,支持重叠、嵌套、非对称排布等灵活效果;
2. 无需遵循网格规则,可根据子图重要性调整尺寸(如核心图表放大,辅助图表缩小);
3. 需手动优化参数避免子图重叠或间距失衡,对布局设计经验要求较高。

二、绘制等分区域子图

等分区域子图是指将画布按照固定行列数均匀分割,每个子图占据相同大小的网格空间,适合需要平等展示多组相关数据的场景。以下分别介绍两种常用的绘制方法:plt.subplot()plt.subplots()

1. 使用 plt.subplot() 绘制子图

plt.subplot() 是 Matplotlib 中创建网格子图的基础方法,通过指定网格的行数、列数和子图索引,逐个创建子图。该方法灵活度高,可按需创建单个或多个子图,适合动态生成子图的场景。

plt.subplot() 方法参数说明:

参数名 作用描述 取值示例 补充说明
nrows 网格的行数 23 决定子图布局的行数,如 nrows=2 表示将画布分为 2 行
ncols 网格的列数 24 决定子图布局的列数,如 ncols=3 表示将画布分为 3 列
index 子图在网格中的索引位置 15 索引从 1 开始,按“行优先”排序(如 2x2 网格的索引顺序为 1→2→3→4)
projection 子图的投影类型 'polar' 可选参数,用于创建特殊投影子图(如极坐标图),默认为笛卡尔坐标系
polar 是否使用极坐标系(简化的 projection TrueFalse 等价于 projection='polar',优先级低于 projection
sharex 与其他子图共享 x 轴 ax 对象 sharex=ax1 表示与 ax1 子图共享 x 轴,减少重复标签
sharey 与其他子图共享 y 轴 ax 对象 sharey=ax2 表示与 ax2 子图共享 y 轴

示例:绘制多个子图

# 导入 matplotlib.pyplot 模块,用于绘图
import matplotlib.pyplot as plt
# 导入 numpy 模块,使用其数值计算功能
import numpy as np

# 使用 numpy 的 linspace 函数生成一个从 0 到 10 的等差数列,共 100 个点
x = np.linspace(0, 10, 100)

# 创建一个大小为 (宽度10英寸, 高度8英寸) 的画布(Figure)
plt.figure(figsize=(10, 8))

# 在画布上创建第一个子图(2行2列中的第1个位置)
# subplot(2, 2, 1) 表示将画布分为2行2列共4个子图,当前操作的是第1个子图
plt.subplot(2, 2, 1)
# 在该子图中绘制 sin(x) 曲线,颜色设置为蓝色
plt.plot(x, np.sin(x), color='blue')
# 设置该子图的标题为 'sin(x)'
plt.title('sin(x)')

# 在画布上创建第二个子图(2行2列中的第2个位置)
plt.subplot(2, 2, 2)
# 在该子图中绘制 cos(x) 曲线,颜色设置为红色
plt.plot(x, np.cos(x), color='red')
# 设置该子图的标题为 'cos(x)'
plt.title('cos(x)')

# 在画布上创建第三个子图(2行2列中的第3个位置)
plt.subplot(2, 2, 3)
# 在该子图中绘制 tan(x) 曲线,颜色设置为绿色
plt.plot(x, np.tan(x), color='green')
# 设置该子图的标题为 'tan(x)'
plt.title('tan(x)')

# 在画布上创建第四个子图(2行2列中的第4个位置)
plt.subplot(2, 2, 4)
# 在该子图中绘制 exp(x/10) 曲线,颜色设置为紫色
plt.plot(x, np.exp(x/10), color='purple')
# 设置该子图的标题为 'exp(x/10)'
plt.title('exp(x/10)')

# 调用 tight_layout 函数自动调整子图之间的间距,避免重叠或过于紧凑
plt.tight_layout()

# 显示整个画布上的所有子图
plt.show()

绘制的图形如下图所示:

在这里插入图片描述

示例:工业月度同比情况

数据如下图所示,需要根据这个数据绘制多个子图。

在这里插入图片描述

实现代码如下所示:

# 导入 matplotlib.pyplot 模块,用于绘图
import matplotlib.pyplot as plt
# 导入 pandas 模块,用于读取和处理 CSV 数据
import pandas as pd

# 设置中文字体为 SimHei(黑体),确保图表中的中文标签(如标题、坐标轴)能正常显示
plt.rcParams['font.sans-serif'] = ['SimHei']

# 解决负号 '-' 显示为方块的问题(默认字体可能不支持负号)
# 设置为 False 表示允许正常显示负号
plt.rcParams['axes.unicode_minus'] = False

# 定义 CSV 文件路径,请根据实际情况替换为你的文件路径
csv_file_path = './data/工业月度同比情况.csv'

# 使用 pandas 读取指定路径的 CSV 文件,返回一个 DataFrame 对象
df = pd.read_csv(csv_file_path)

# 从 DataFrame 中提取“月份”列,并转换为 Python 列表,用于后续绘图的 x 轴数据
month = df['月份'].tolist()

# 提取“规模以上工业同比增速(%)”列数据,转换为列表
industry_speed = df['规模以上工业同比增速(%)'].tolist()

# 提取“钢材同比增速(%)”列数据,转换为列表
steel_speed = df['钢材同比增速(%)'].tolist()

# 提取“十种有色金属同比增速(%)”列数据,转换为列表
metals_speed = df['十种有色金属同比增速(%)'].tolist()

# 创建第一个子图:位于 3 行 1 列布局中的第 1 个位置
plt.subplot(3, 1, 1)
# 绘制折线图:x 轴为 month,y 轴为 industry_speed
# 颜色为红色,数据点用圆形标记(marker='o'),标记大小为 3
plt.plot(month, industry_speed, color='red', marker='o', markersize=3)
# 设置该子图的标题
plt.title('规模以上工业同比增速')
# 设置 y 轴标签
plt.ylabel('增长速度(%)')
# 设置 y 轴的数值范围为 0 到 10,便于统一尺度观察趋势
plt.ylim(0, 10)
# 在每个数据点上方添加文本标签,显示具体数值
for i in range(len(df)):
    plt.text(
        month[i],  # x 坐标:对应月份
        industry_speed[i],  # y 坐标:对应增速值
        s=industry_speed[i],  # 显示的文本内容:增速数值
        ha='center',  # 水平对齐方式:居中对齐
        va='bottom'  # 垂直对齐方式:文本在点的下方(向上显示)
    )

# 创建第二个子图:位于 3 行 1 列布局中的第 2 个位置
plt.subplot(3, 1, 2)
# 绘制钢材增速折线图,颜色为绿色
plt.plot(month, steel_speed, color='green', marker='o', markersize=3)
plt.title('钢材同比增长速度')
plt.ylabel('增长速度(%)')
# 设置 y 轴范围:从 -5 到 20,因为钢材增速可能出现负值
plt.ylim(-5, 20)
# 为每个数据点添加数值标签
for i in range(len(df)):
    plt.text(
        month[i],
        steel_speed[i],
        s=steel_speed[i],
        ha='center',
        va='bottom'
    )

# 创建第三个子图:位于 3 行 1 列布局中的第 3 个位置
plt.subplot(3, 1, 3)
# 绘制十种有色金属增速折线图,颜色为蓝色
plt.plot(month, metals_speed, color='blue', marker='o', markersize=3)
plt.title('十种有色金属同比增长速度')
plt.ylabel('增长速度(%)')
# 设置 y 轴范围:0 到 15
plt.ylim(0, 15)
# 为每个数据点添加数值标签
for i in range(len(df)):
    plt.text(
        month[i],
        metals_speed[i],
        s=metals_speed[i],
        ha='center',
        va='bottom'
    )

# 自动调整子图之间的间距,防止标题、标签等元素重叠
plt.tight_layout()

# 显示绘制好的图形窗口
plt.show()

绘制的图形如下图所示:

在这里插入图片描述

2. 使用 plt.subplots() 绘制子图

plt.subplots() 是创建网格子图的高效方法,一次性创建指定行列数的所有子图,并返回画布(Figure)和子图数组(Axes)。该方法适合批量创建结构固定的子图,代码更简洁,便于统一管理。

plt.subplots() 方法参数说明:

参数名 作用描述 取值示例 补充说明
nrows 网格的行数(默认 1) 23 nrows=2 表示创建 2 行子图
ncols 网格的列数(默认 1) 24 ncols=3 表示创建 3 列子图
figsize 画布的尺寸(宽, 高),单位为英寸 (10, 8) 控制整体画布大小,影响所有子图的显示比例
dpi 画布的分辨率(每英寸像素数) 100150 默认值为 100,数值越大图像越清晰
sharex 子图是否共享 x 轴 True'col' True/'all' 表示所有子图共享;'col' 表示每列子图共享;False 不共享
sharey 子图是否共享 y 轴 True'row' True/'all' 表示所有子图共享;'row' 表示每行子图共享;False 不共享
hspace 子图之间的垂直间距(相对高度比例) 0.30.5 默认值为 0.2,数值越大间距越宽
wspace 子图之间的水平间距(相对宽度比例) 0.30.5 默认值为 0.2,数值越大间距越宽
subplot_kw 传递给子图的关键字参数(如 projection {'projection': 'polar'} 用于为所有子图统一设置属性(如都使用极坐标)

示例:绘制多个子图

# 导入 matplotlib.pyplot 模块,用于绘图
import matplotlib.pyplot as plt
# 导入 numpy 模块,使用其数值计算功能
import numpy as np

# 使用 numpy 的 linspace 函数生成一个从 0 到 10 的等差数列,共 100 个点,作为 x 轴的数据
x = np.linspace(0, 10, 100)

# 创建一个包含 2 行 2 列(共 4 个)子图的网格,并返回图形对象 fig 和子图数组 axes
# 注意:这里的 nrows=2 和 ncols=2 实际创建的是 2 行 2 列的布局,但代码注释中提到的是 2 行 3 列,可能是注释错误
fig, axes = plt.subplots(nrows=2, ncols=2)

# 第 1 行第 1 列位置的子图绘制 sin(x) 曲线,颜色设置为蓝色
axes[0, 0].plot(x, np.sin(x), color='blue')
axes[0, 0].set_title('sin(x)')  # 设置该子图标题为 'sin(x)'

# 第 1 行第 2 列位置的子图绘制 cos(x) 曲线,颜色设置为紫色
axes[0, 1].plot(x, np.cos(x), color='purple')
axes[0, 1].set_title('cos(x)')  # 设置该子图标题为 'cos(x)'

# 第 2 行第 1 列位置的子图绘制 tan(x) 曲线,颜色设置为橙色
axes[1, 0].plot(x, np.tan(x), color='orange')
axes[1, 0].set_title('tan(x)')  # 设置该子图标题为 'tan(x)'

# 第 2 行第 2 列位置的子图绘制 exp(x/10) 曲线,颜色设置为棕色
axes[1, 1].plot(x, np.exp(x / 10), color='brown')
axes[1, 1].set_title('exp(x/10)')  # 设置该子图标题为 'exp(x/10)'

# 调用 tight_layout 函数自动调整子图之间的间距,避免重叠或过于紧凑
plt.tight_layout()

# 显示整个画布上的所有子图
plt.show()

绘制的图形如下图所示:

在这里插入图片描述

示例:部分国家养猫与养狗人群比例

数据如下图所示,需要根据这个数据绘制多个子图。

在这里插入图片描述

实现代码如下所示:

# 导入 matplotlib.pyplot 模块,用于绘图
import matplotlib.pyplot as plt
# 导入 numpy 模块,用于数值计算和数组操作
import numpy as np
# 导入 pandas 模块,用于读取和处理 CSV 数据
import pandas as pd

# 设置中文字体为 SimHei(黑体),确保图表中的中文标签(如标题、坐标轴、图例)能正常显示
plt.rcParams['font.sans-serif'] = ['SimHei']

# 解决负号 '-' 显示为方块的问题(某些中文字体不支持负号)
# 设置为 False 表示允许正常显示负号
plt.rcParams['axes.unicode_minus'] = False

# 定义 CSV 文件路径,请根据实际情况替换为你的实际文件路径
csv_file_path = './data/部分国家养猫与养狗人群比例.csv'

# 使用 pandas 读取指定路径的 CSV 文件,返回一个 DataFrame 对象(二维表格结构)
df = pd.read_csv(csv_file_path)

# 从 DataFrame 中提取“国家”列,并转换为 Python 列表
# 用于后续作为条形图的 y 轴标签(国家名称)
country = df['国家'].tolist()

# 提取“养猫人群比例(%)”列数据,转换为列表
# 用于绘制养猫比例的横向条形图
x_cat = df['养猫人群比例(%)'].tolist()

# 提取“养狗人群比例(%)”列数据,转换为列表
# 用于绘制养狗比例的横向条形图
x_dog = df['养狗人群比例(%)'].tolist()

# 创建一个包含 1 行 2 列子图的图形画布
# fig: 整个图形对象,用于控制整体布局
# ax: 包含两个 Axes 对象的数组,分别对应左右两个子图
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(10, 6))  # 可选:设置画布大小

# 绘制左侧子图:养猫人群比例的横向条形图(barh 表示 horizontal bar)
ax[0].barh(
    y=np.arange(len(country)),  # y 轴位置:用 0,1,2... 表示每个国家的位置
    width=x_cat,  # 条形的长度(宽度),对应养猫比例数值
    height=0.5,  # 条形的高度(厚度),可调整美观度
    tick_label=country,  # y 轴刻度标签,显示国家名称
    color='#ffa2a5'  # 条形颜色,使用十六进制浅红色调(粉红色)
)
ax[0].set_title('部分国家养猫人群比例')  # 设置左侧子图标题
ax[0].set_xlabel('人群比例(%)')  # 设置 x 轴标签
# 为左侧子图的每个条形添加数值标签
for i in range(len(country)):
    ax[0].text(x=x_cat[i], y=i, s=f'{i}%', ha='left', va='center')

# 绘制右侧子图:养狗人群比例的横向条形图
ax[1].barh(
    y=np.arange(len(country)),  # y 轴位置与左侧一致,确保国家对齐
    width=x_dog,  # 条形长度对应养狗比例
    height=0.5,  # 条形高度
    tick_label=country,  # 显示国家名称
    color='#a2d5f5'  # 条形颜色,使用十六进制浅蓝色调
)
ax[1].set_title('部分国家养狗人群比例')  # 设置右侧子图标题
ax[1].set_xlabel('人群比例(%)')  # 设置 x 轴标签
# 为右侧子图的每个条形添加数值标签
for i in range(len(country)):
    ax[1].text(x=x_dog[i], y=i, s=f'{i}%', ha='left', va='center')

# 自动调整子图之间的间距,防止标题、标签等元素重叠或显示不全
plt.tight_layout()

# 显示绘制好的图形窗口
plt.show()

绘制的图形如下图所示:

在这里插入图片描述


三、绘制跨越区域子图

1. 使用 plt.subplot2grid() 绘制子图

plt.subplot2grid() 允许将画布划分为一个二维网格,并通过指定起始位置和跨越范围来创建子图,非常适合制作复杂布局的可视化面板(如仪表盘)。

plt.subplot2grid()方法参数说明:

参数名 类型 描述 示例
shape tuple (rows, cols) 定义整个画布被划分为多少行和列的网格 (3, 3) 表示 3 行 3 列
loc tuple (row, col) 指定子图左上角所在的网格位置(从 (0,0) 开始) (0, 0) 表示第一行第一列
rowspan int(可选) 子图纵向跨越的行数 2 表示占两行高度
colspan int(可选) 子图横向跨越的列数 2 表示占两列宽度
**kwargs 其他参数 传递给 Axes 的参数,如 figsize、投影方式等 projection='polar'

2. 示例:抖音和快手用户画像对比

数据如下图所示,需要根据这两个数据绘制跨区域子图。

在这里插入图片描述

实现代码如下所示:

# 导入 matplotlib.pyplot 模块,用于绘图
import matplotlib.pyplot as plt
# 导入 numpy 模块,用于数值计算和数组操作(例如生成坐标位置)
import numpy as np
# 导入 pandas 模块,用于读取和处理 CSV 数据
import pandas as pd

# 设置中文字体为 SimHei(黑体),确保图表中的中文标签(如标题、坐标轴、图例)能正常显示
plt.rcParams['font.sans-serif'] = ['SimHei']

# 解决负号 '-' 显示为方块的问题(某些中文字体不支持负号)
# 设置为 False 表示允许正常显示负号
plt.rcParams['axes.unicode_minus'] = False

# 定义两个 CSV 文件的路径,请根据实际情况替换为你的实际文件路径
csv_file_path1 = './data/抖音和快手平台用户城市分布.csv'  # 用户城市分布数据
csv_file_path2 = './data/抖音和快手平台用户年龄分布.csv'  # 用户年龄分布数据

# 使用 pandas 读取两个 CSV 文件,返回两个 DataFrame 对象(二维表格结构)
df1 = pd.read_csv(csv_file_path1)  # 城市分布数据
df2 = pd.read_csv(csv_file_path2)  # 年龄分布数据

# 为城市分布图生成 x 轴的位置索引(0, 1, 2...),用于控制柱子的水平位置
x1 = np.arange(len(df1))
# 为年龄分布图生成 y 轴的位置索引(0, 1, 2...),用于控制条形的垂直位置
x2 = np.arange(len(df2))

# 提取城市分类标签(如:一线城市、二线城市等),用于 x 轴刻度标签
city_labels = df1['城市分类'].tolist()
# 提取年龄区间标签(如:18-24岁、25-34岁等),用于 y 轴刻度标签
age_labels = df2['年龄区间'].tolist()

# 提取抖音平台在不同城市的用户比例数据
douyin_data1 = df1['抖音用户比例(%)'].tolist()
# 提取快手平台在不同城市的用户比例数据
kuaishou_data1 = df1['快手用户比例(%)'].tolist()

# 提取抖音平台在不同年龄区间的用户比例数据
douyin_data2 = df2['抖音用户比例(%)'].tolist()
# 提取快手平台在不同年龄区间的用户比例数据
kuaishou_data2 = df2['快手用户比例(%)'].tolist()

# -------------------------------
# 第一个子图:城市分布对比(顶部横跨两列)
# 使用 subplot2grid 创建自定义布局:整个画布划分为 3 行 2 列
# 当前子图从 (0,0) 开始(第1行第1列),横向占2列,纵向占1行
ax1 = plt.subplot2grid((3, 2), (0, 0), rowspan=1, colspan=2)

# 绘制抖音用户比例的柱状图
ax1.bar(
    x=x1,  # 柱子的 x 坐标位置
    height=douyin_data1,  # 柱子的高度(用户比例)
    color='#a2c4ff',  # 颜色:浅蓝色(代表抖音)
    tick_label=city_labels,  # x 轴刻度标签:城市分类名称
    width=0.3,  # 柱子宽度
    label='抖音'  # 图例标签
)

# 绘制快手用户比例的柱状图,x 位置向右偏移 0.3,实现并列显示
ax1.bar(
    x=x1 + 0.3,  # 向右偏移,避免与抖音柱子重叠
    height=kuaishou_data1,  # 快手用户比例
    color='#ffa2c4',  # 颜色:浅粉色(代表快手)
    tick_label=city_labels,  # 标签与抖音一致
    width=0.3,  # 柱子宽度
    label='快手'  # 图例标签
)

# 设置标题
ax1.set_title('抖音和快手平台用户城市分布')
# 设置 x 轴标签
ax1.set_xlabel('城市分类')
# 设置 y 轴标签
ax1.set_ylabel('用户比例(%)')
# 添加图例,设置字体大小为 8
ax1.legend(fontsize=8)
# 设置 y 轴范围为 0 到 25%,便于统一比较
ax1.set_ylim(0, 25)

# 在每个柱子顶部添加数值标签(抖音)
for i in range(len(df1)):
    ax1.text(
        x=x1[i],  # x 位置
        y=douyin_data1[i],  # y 位置(柱子顶部)
        s=douyin_data1[i],  # 显示的文本内容
        ha='center',  # 水平居中对齐
        va='bottom'  # 垂直底部对齐(在柱子上方)
    )
    # 添加快手数据标签
    ax1.text(
        x=x1[i] + 0.3,
        y=kuaishou_data1[i],
        s=kuaishou_data1[i],
        ha='center',
        va='bottom'
    )

# -------------------------------
# 第二个子图:抖音平台用户年龄分布(左下角)
# 位置:从 (1,0) 开始(第2行第1列),纵向占2行,横向占1列
ax2 = plt.subplot2grid((3, 2), (1, 0), rowspan=2, colspan=1)

# 绘制横向条形图(barh),展示抖音用户在各年龄段的比例
ax2.barh(
    y=x2,  # 条形的 y 位置
    width=douyin_data2,  # 条形长度(用户比例)
    height=0.5,  # 条形高度(厚度)
    tick_label=age_labels,  # y 轴标签:年龄区间
    color='#a2c4ff'  # 浅蓝色,与上图一致
)

# 设置标题
ax2.set_title('抖音平台用户年龄分布')
# 设置 x 轴标签
ax2.set_xlabel('用户比例(%)')
# 设置 x 轴范围为 0 到 30%,便于比较
ax2.set_xlim(0, 30)

# 在每个条形右侧添加数值标签
for i in range(len(df2)):
    ax2.text(
        x=douyin_data2[i],  # x 位置:比例值
        y=x2[i],  # y 位置:对应年龄区间
        s=douyin_data2[i],  # 显示的数值
        ha='left',  # 水平左对齐(在条形右侧)
        va='center'  # 垂直居中对齐
    )

# -------------------------------
# 第三个子图:快手平台用户年龄分布(右下角)
# 位置:从 (1,1) 开始(第2行第2列),纵向占2行,横向占1列
ax3 = plt.subplot2grid((3, 2), (1, 1), rowspan=2, colspan=1)

# 绘制横向条形图,展示快手用户在各年龄段的比例
ax3.barh(
    y=x2,
    width=kuaishou_data2,
    height=0.5,
    tick_label=age_labels,
    color='#ffa2c4'  # 浅粉色,与上图一致
)

# 设置标题
ax3.set_title('快手平台用户年龄分布')
# 设置 x 轴标签
ax3.set_xlabel('用户比例(%)')
# 设置 x 轴范围一致,便于对比
ax3.set_xlim(0, 30)

# 在每个条形右侧添加数值标签
for i in range(len(df2)):
    ax3.text(
        x=kuaishou_data2[i],
        y=x2[i],
        s=kuaishou_data2[i],
        ha='left',
        va='center'
    )

# 自动调整所有子图之间的间距,防止标题、标签、图例等元素重叠或被截断
plt.tight_layout()

# 显示绘制好的图形窗口
plt.show()

绘制的图形如下图所示:

在这里插入图片描述


四、绘制自定义区域子图

在 Matplotlib 中,除了使用 subplot()subplot2grid() 创建规则布局外,还可以通过更灵活的机制实现高度自定义的子图区域划分。本节将介绍如何使用 GridSpec 类和 add_gridspec() 方法来创建复杂的、可定制间距与比例的子图布局。

1. 通过 GridSpec 类绘制

GridSpec 是一个用于定义网格布局的类,可以指定行数、列数以及每行/列的尺寸比例和间距。

GridSpec 类构造方法参数说明:

参数名 类型 描述 示例
nrows int 网格的行数 3
ncols int 网格的列数 2
figure Figure(可选) 关联的画布对象 fig
left, right, top, bottom float(可选) 子图区域距离画布边缘的位置(归一化坐标 0~1) left=0.1, right=0.9
wspace float(可选) 列之间的水平间距 0.3
hspace float(可选) 行之间的垂直间距 0.4
width_ratios list of floats 每列的相对宽度比例 [2, 1] 表示第一列是第二列的两倍宽
height_ratios list of floats 每行的相对高度比例 [1, 3] 表示第二行是第一行的三倍高

🔹 所有间距值为 None 时表示自动调整(默认);
🔹 width_ratiosheight_ratios 使用比例而非绝对值。

示例:使用 GridSpec 创建布局

# 导入 matplotlib.gridspec 模块,用于创建复杂的、可自定义行列比例的子图网格布局
import matplotlib.gridspec as gridspec
# 导入 matplotlib.pyplot 模块,用于绘图
import matplotlib.pyplot as plt

# 创建一个画布(Figure),设置图形大小为 8 英寸宽 × 6 英寸高
fig = plt.figure(figsize=(8, 6))

# 使用 GridSpec 定义一个 3 行 3 列的网格布局(共 9 个单元格)
# 该布局将用于在画布上精确控制每个子图的位置和大小
gs = gridspec.GridSpec(
    nrows=3,  # 网格的行数:3 行
    ncols=3,  # 网格的列数:3 列
    figure=fig,  # 指定该 GridSpec 属于哪个画布对象
    wspace=0.2,  # 子图之间的横向间距(空白宽度比例),0.2 表示中等间距
    hspace=0.2,  # 子图之间的纵向间距(空白高度比例)
    width_ratios=[1, 1, 1],  # 每一列的相对宽度比例:三列等宽
    height_ratios=[1, 1, 1]  # 每一行的相对高度比例:三行等高
)

# 在网格的第 1 行(索引 0),占据所有 3 列(切片 0:3)创建第一个子图
ax1 = fig.add_subplot(gs[0, 0:3])
# 在 ax1 中绘制一条简单的折线(x: 0~4, y: [1,2,3,4,5])
ax1.plot([1, 2, 3, 4, 5])

# 在网格的第 2 行(索引 1),占据前 2 列(0~1列)创建第二个子图
ax2 = fig.add_subplot(gs[1, 0:2])
ax2.plot([1, 2, 3, 4, 5])

# 在网格的第 3 行(索引 2),占据第 1 列(索引 0)创建第三个子图
ax3 = fig.add_subplot(gs[2, :1])  # 等价于 gs[2, 0]
ax3.plot([1, 2, 3, 4, 5])

# 在网格的第 3 行(索引 2),占据第 2 列(索引 1)创建第四个子图
ax4 = fig.add_subplot(gs[2, 1:2])  # 注意:切片 1:2 表示只取第1列(索引1)
ax4.plot([1, 2, 3, 4, 5])

# 在网格的第 2-3 行(索引 1 到 2),占据第 3 列(索引 2)创建第五个子图
# 这是一个跨行的子图(纵向合并两个单元格)
ax5 = fig.add_subplot(gs[1:3, 2])
ax5.plot([1, 2, 3, 4, 5])

# 显示图形
plt.show()

绘制的图形如下图所示:

在这里插入图片描述

2. 通过 add_gridspec() 方法绘制

Figure.add_gridspec() 是 Matplotlib 3.1+ 推荐的现代方法,它直接在画布上创建一个 GridSpec 实例,并返回该对象,便于链式调用。

add_gridspec()方法参数说明:

参数名 类型 描述 示例
nrows int 行数 2
ncols int 列数 3
left, right, top, bottom float 子图区域边界(归一化坐标) left=0.1
wspace, hspace float 水平/垂直间距 wspace=0.2
width_ratios list 各列宽度比例 [1, 2, 1]
height_ratios list 各行高度比例 [3, 1]

示例:使用 add_gridspec() 创建布局

# 导入 matplotlib.pyplot 模块,用于绘图
import matplotlib.pyplot as plt

# 创建一个图形画布(Figure),并设置其大小为 8 英寸宽 × 6 英寸高
# figsize 参数控制整个图形的尺寸,便于后续排版和展示
fig = plt.figure(figsize=(8, 6))

# 在画布 fig 上添加一个 GridSpec(网格规范)对象,用于定义子图的网格布局
# 这种方式比直接使用 gridspec.GridSpec 更简洁,是 matplotlib 推荐的现代写法
gs = fig.add_gridspec(
    nrows=3,  # 定义网格有 3 行
    ncols=3,  # 定义网格有 3 列
    figure=fig,  # 指定该 GridSpec 所属的画布对象(通常可省略,因为是通过 fig 调用的)
    wspace=0.2,  # 设置子图之间的横向间距(width space)
    hspace=0.2,  # 设置子图之间的纵向间距(height space)
    width_ratios=[1, 1, 1],  # 每一列的相对宽度比例
    height_ratios=[1, 1, 1]  # 每一行的相对高度比例
)

# 在网格的第 1 行(索引 0),占据所有 3 列(切片 0:3)创建第一个子图
ax1 = fig.add_subplot(gs[0, 0:3])
# 在 ax1 中绘制一条简单的折线(x: 0~4, y: [1,2,3,4,5])
ax1.plot([1, 2, 3, 4, 5])

# 在网格的第 2 行(索引 1),占据前 2 列(0~1列)创建第二个子图
ax2 = fig.add_subplot(gs[1, 0:2])
ax2.plot([1, 2, 3, 4, 5])

# 在网格的第 3 行(索引 2),占据第 1 列(索引 0)创建第三个子图
ax3 = fig.add_subplot(gs[2, :1])  # 等价于 gs[2, 0]
ax3.plot([1, 2, 3, 4, 5])

# 在网格的第 3 行(索引 2),占据第 2 列(索引 1)创建第四个子图
ax4 = fig.add_subplot(gs[2, 1:2])  # 注意:切片 1:2 表示只取第1列(索引1)
ax4.plot([1, 2, 3, 4, 5])

# 在网格的第 2-3 行(索引 1 到 2),占据第 3 列(索引 2)创建第五个子图
# 这是一个跨行的子图(纵向合并两个单元格)
ax5 = fig.add_subplot(gs[1:3, 2])
ax5.plot([1, 2, 3, 4, 5])

# 显示图形
plt.show()

绘制的图形如下图所示:

在这里插入图片描述


五、子图坐标轴共享

在创建多个子图时,有时候需要共享某些坐标轴(x轴或y轴)以便于比较不同数据集的趋势或分布。Matplotlib 提供了便捷的方法来实现这一点,即通过 plt.subplot()plt.subplots() 中的 sharexsharey 参数来控制坐标轴共享。

1. 共享相邻子图的坐标轴

1.1 共享相邻子图的坐标轴介绍

共享坐标轴能够确保相关子图之间的一致性,这对于比较不同的数据集尤其有用。例如,在时间序列分析中,可能希望所有子图共享相同的x轴以展示不同变量随时间的变化趋势;或者在散点图矩阵中,每个子图都共享相同的x轴和y轴范围,以便于对比不同变量之间的关系。

共享坐标轴不仅可以减少重复工作,还能提高图形的可读性和一致性。此外,当调整一个共享坐标轴的缩放比例或移动视图窗口时,其他共享该坐标轴的子图也会同步更新,增强了交互式探索数据的能力。

1.2 plt.subplot()plt.subplots() 方法中的参数 sharexsharey 的取值

在 Matplotlib 中,plt.subplot()plt.subplots() 方法允许通过设置 sharexsharey 参数来控制坐标轴的共享方式。以下是这些参数可能的取值及其含义:

参数 sharexsharey 的取值说明:

取值 描述
"none" 不共享任何坐标轴(默认)。
"all" 所有子图共享同一个x轴或y轴。这意味着所有子图都将具有相同的轴刻度和范围。
"row" 同一行中的子图共享x轴或y轴。适用于需要在同一行内进行直接比较的情况。
"col" 同一列中的子图共享x轴或y轴。适用于需要在同一列内进行直接比较的情况。
"auto" 根据布局自动决定是否共享坐标轴。通常不会主动选择此选项,因为它依赖于具体的上下文。

1.3 示例:部分国家养猫与养狗人群比例

数据如下图所示,需要根据这个数据绘制多个子图,并共享y轴。

在这里插入图片描述

实现代码如下所示:

# 导入 matplotlib.pyplot 模块,用于绘图
import matplotlib.pyplot as plt
# 导入 numpy 模块,用于数值计算和数组操作
import numpy as np
# 导入 pandas 模块,用于读取和处理 CSV 数据
import pandas as pd

# 设置中文字体为 SimHei(黑体),确保图表中的中文标签(如标题、坐标轴、图例)能正常显示
plt.rcParams['font.sans-serif'] = ['SimHei']

# 解决负号 '-' 显示为方块的问题(某些中文字体不支持负号)
# 设置为 False 表示允许正常显示负号
plt.rcParams['axes.unicode_minus'] = False

# 定义 CSV 文件路径,请根据实际情况替换为你的实际文件路径
csv_file_path = './data/部分国家养猫与养狗人群比例.csv'

# 使用 pandas 读取指定路径的 CSV 文件,返回一个 DataFrame 对象(二维表格结构)
df = pd.read_csv(csv_file_path)

# 从 DataFrame 中提取“国家”列,并转换为 Python 列表
# 用于后续作为条形图的 y 轴标签(国家名称)
country = df['国家'].tolist()

# 提取“养猫人群比例(%)”列数据,转换为列表
# 用于绘制养猫比例的横向条形图
x_cat = df['养猫人群比例(%)'].tolist()

# 提取“养狗人群比例(%)”列数据,转换为列表
# 用于绘制养狗比例的横向条形图
x_dog = df['养狗人群比例(%)'].tolist()

# 创建一个包含 1 行 2 列子图的图形画布
# fig: 整个图形对象,用于控制整体布局
# ax: 包含两个 Axes 对象的数组,分别对应左右两个子图
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(8, 6), sharey='row')

# 绘制左侧子图:养猫人群比例的横向条形图(barh 表示 horizontal bar)
ax[0].barh(
    y=np.arange(len(country)),  # y 轴位置:用 0,1,2... 表示每个国家的位置
    width=x_cat,  # 条形的长度(宽度),对应养猫比例数值
    height=0.5,  # 条形的高度(厚度),可调整美观度
    tick_label=country,  # y 轴刻度标签,显示国家名称
    color='#ffa2a5'  # 条形颜色,使用十六进制浅红色调(粉红色)
)
ax[0].set_title('部分国家养猫人群比例')  # 设置左侧子图标题
ax[0].set_xlabel('人群比例(%)')  # 设置 x 轴标签
# 为左侧子图的每个条形添加数值标签
for i in range(len(country)):
    ax[0].text(x=x_cat[i], y=i, s=f'{i}%', ha='left', va='center')

# 绘制右侧子图:养狗人群比例的横向条形图
ax[1].barh(
    y=np.arange(len(country)),  # y 轴位置与左侧一致,确保国家对齐
    width=x_dog,  # 条形长度对应养狗比例
    height=0.5,  # 条形高度
    tick_label=country,  # 显示国家名称
    color='#a2d5f5'  # 条形颜色,使用十六进制浅蓝色调
)
ax[1].set_title('部分国家养狗人群比例')  # 设置右侧子图标题
ax[1].set_xlabel('人群比例(%)')  # 设置 x 轴标签
# 为右侧子图的每个条形添加数值标签
for i in range(len(country)):
    ax[1].text(x=x_dog[i], y=i, s=f'{i}%', ha='left', va='center')

# 自动调整子图之间的间距,防止标题、标签等元素重叠或显示不全
plt.tight_layout()

# 显示绘制好的图形窗口
plt.show()

绘制的图形如下图所示:

在这里插入图片描述

2. 共享非相邻子图的坐标轴

2.1 共享非相邻子图的坐标轴介绍

在 Matplotlib 中,除了共享相邻子图(如同行或同列)的坐标轴外,有时也需要让不相邻的子图(例如分散在画布不同位置的子图)共享同一个坐标轴(x 轴或 y 轴)。这种需求常见于复杂布局中,例如仪表盘、多面板可视化或需要跨区域对比数据的场景。

plt.subplots(sharex=..., sharey=...) 仅适用于规则网格布局不同,共享非相邻子图的坐标轴通常需要通过手动指定 sharexsharey 参数,将一个子图(Axes)作为另一个子图的“参考轴”,从而实现坐标轴的联动。

2.2 示例:非相邻子图坐标轴共享

# 导入必要的库
import matplotlib.pyplot as plt
import numpy as np

# 使用 subplot 函数创建一个 2x2 的网格布局中的第一个子图 (即左上角位置)
# 221 表示在 2 行 2 列的网格中的第 1 个位置
ax_one = plt.subplot(221)

# 生成 x 轴数据,范围从 0 到 2π,共 400 个点
x1 = np.linspace(0, 2 * np.pi, 400)
# 根据 x1 计算 y 轴数据,这里使用 cos(x^2) 函数
y1 = np.cos(x1 ** 2)

# 在 ax_one 子图中绘制曲线
ax_one.plot(x1, y1)

# 生成第二个 x 轴的数据集,范围从 0.01 到 10,共 100 个点
x2 = np.linspace(0.01, 10, 100)
# 根据 x2 计算 y 轴数据,这里使用 sin(x) 函数
y2 = np.sin(x2)

# 创建 2x2 网格布局中的第四个子图 (即右下角位置),并指定与 ax_one 共享 x 轴
# 这意味着 ax_two 将具有与 ax_one 相同的 x 轴缩放和范围
ax_two = plt.subplot(224, sharex=ax_one)

# 在 ax_two 子图中绘制曲线
ax_two.plot(x2, y2)

# 显示图形
plt.show()

绘制的图形如下图所示:

在这里插入图片描述

3. 同一子图共享坐标轴(双 Y 轴)

3.1 同一子图共享 x 坐标轴介绍

在 Matplotlib 中,“同一子图共享 x 坐标轴” 实际上是指:在同一个图表区域(Axes)中,创建两个独立的 Y 轴(左和右),但共享同一个 X 轴。这种技术通常被称为 “双 Y 轴图”(Dual Y-axis Plot),是数据可视化中非常常见的需求。

可使用ax.twinx()方法实现同一子图共享 x 坐标轴。

方法 描述 返回值 是否有参数
ax.twinx() 创建一个共享 x 轴、但拥有独立 y 轴 的新坐标系(通常在右侧) 新的 Axes 对象 ❌ 无参数

3.2 示例:某地区全年气温与降水量、蒸发量的关系

数据如下图所示,需要根据这个数据绘制双y轴的图形。

在这里插入图片描述

实现代码如下所示:

# 导入 matplotlib.pyplot 模块,用于绘图
import matplotlib.pyplot as plt
# 导入 numpy 模块,用于数值计算和数组操作
import numpy as np
# 导入 pandas 模块,用于读取和处理 CSV 数据
import pandas as pd

# 设置中文字体为 SimHei(黑体),确保图表中的中文标签(如标题、坐标轴、图例)能正常显示
plt.rcParams['font.sans-serif'] = ['SimHei']

# 解决负号 '-' 显示为方块的问题(某些中文字体不支持负号)
# 设置为 False 表示允许正常显示负号
plt.rcParams['axes.unicode_minus'] = False

# 定义 CSV 文件路径,请根据实际情况替换为你的实际文件路径
csv_file_path = './data/某地区全年的平均气温与降水量、蒸发量 .csv'

# 使用 pandas 读取指定路径的 CSV 文件,返回一个 DataFrame 对象(二维表格结构)
df = pd.read_csv(csv_file_path)

# 从 DataFrame 中提取 '月份' 列,并将其转换为 Python 列表
month = df['月份'].tolist()

# 从 DataFrame 中提取 '蒸发量 (ml)' 列,并将其转换为 Python 列表
evaporation = df['蒸发量 (ml)'].tolist()

# 从 DataFrame 中提取 '降水量 (ml)' 列,并将其转换为 Python 列表
rainfall = df['降水量 (ml)'].tolist()

# 从 DataFrame 中提取 '平均气温 (℃)' 列,并将其转换为 Python 列表
temperature = df['平均气温 (℃)'].tolist()

# 创建图形和子图对象,fig 是整个图像窗口,ax 是主图的坐标轴对象
fig, ax = plt.subplots()

# 在主图上绘制蒸发量柱状图,设置宽度和颜色,并添加标签
ax.bar(x=np.arange(len(df)), height=evaporation, width=0.3, label='蒸发量', color='#ffa565', tick_label=month)
# 在主图上偏移0.3的位置绘制降水量柱状图,避免两组柱状图重叠
ax.bar(x=np.arange(len(df)) + 0.3, height=rainfall, width=0.3, label='降水量', color='#65a5ff')

# 设置X轴标签为“月份”
ax.set_xlabel('月份')
# 设置Y轴标签为“水量 (ml)”表示蒸发量和降水量
ax.set_ylabel('水量 (ml)')
# 设置图表标题
ax.set_title('某地区全年的平均气温与降水量、蒸发量')

# 创建一个新的坐标轴 ax_new,与原坐标轴共享X轴,用于展示平均气温数据
ax_new = ax.twinx()
# 在新坐标轴上绘制平均气温折线图,并添加标记点
ax_new.plot(month, temperature, color='#a565ff', marker='o', label='平均气温')
# 设置新坐标轴的Y轴标签为“温度 (℃)”
ax_new.set_ylabel('温度 (℃)')

# 获取两个坐标轴上的图例句柄和标签,合并后一次性添加到图中,以确保所有元素都能正确显示在图例中
handles1, labels1 = ax.get_legend_handles_labels()
handles2, labels2 = ax_new.get_legend_handles_labels()
ax.legend(handles=handles1 + handles2, labels=labels1 + labels2)

# 自动调整子图之间的间距,防止标题、标签等元素重叠或显示不全
plt.tight_layout()

# 显示绘制好的图形窗口
plt.show()

绘制的图形如下图所示:

在这里插入图片描述


六、子图布局

1. 约束布局

在 Matplotlib 中,合理安排多个子图的布局对于提高图表的可读性和美观性至关重要。约束布局(Constrained Layout)是 Matplotlib 提供的一种自动调整子图位置以避免重叠和优化空间利用的功能。

1.1 约束布局介绍

约束布局是一种用于自动化管理子图间距和位置的方法,旨在减少手动调整子图布局的需求。它通过计算每个子图的最佳位置来确保:

  • 子图之间不会重叠;
  • 标题、标签、刻度等元素有足够空间显示,而不会被裁剪或遮挡;
  • 整体布局更加紧凑且有序。

约束布局特别适用于包含多个子图、颜色条、注释和其他复杂元素的图表。它可以帮助用户创建既美观又专业的可视化结果,无需反复试验不同的参数设置。

1.2 启用约束布局

启用约束布局非常简单,可以通过两种主要方式实现:

1.2.1 全局启用

可以在调用 plt.subplots() 或其他绘图函数之前,通过 plt.rcParams 设置全局使用约束布局。

import matplotlib.pyplot as plt

plt.rcParams['figure.constrained_layout.use'] = True

这种方式将影响所有后续生成的图表,除非在特定的图表中明确禁用约束布局。

1.2.2 局部启用

也可以针对具体的图表启用约束布局,通过向 plt.subplots() 或其他类似函数传递 constrained_layout=True 参数。

import matplotlib.pyplot as plt

fig, axs = plt.subplots(2, 2, constrained_layout=True)

这种方法允许对单个图表进行更精细的控制,不影响其他图表的默认行为。

1.2.3 自定义约束布局参数

除了简单的启用/禁用之外,还可以通过 constrained_layout_pads 参数进一步自定义约束布局的行为。

# 导入 matplotlib.pyplot 模块两次是不必要的,这里保留一次即可。
import matplotlib.pyplot as plt

# 设置全局参数以控制图形布局和子图间距
plt.rcParams['figure.constrained_layout.use'] = True  # 启用 constrained_layout 布局管理器,自动调整子图位置避免重叠
plt.rcParams['figure.constrained_layout.w_pad'] = 0.04167  # 子图之间的最小宽度间距(相对字体大小)
plt.rcParams['figure.constrained_layout.h_pad'] = 0.04167  # 子图之间的最小高度间距(相对字体大小)
plt.rcParams['figure.constrained_layout.wspace'] = 0.02  # 相邻子图在水平方向上的间距(相对宽度的比例)
plt.rcParams['figure.constrained_layout.hspace'] = 0.02  # 相邻子图在垂直方向上的间距(相对高度的比例)

# 创建一个包含 2 行 2 列子图的图形(即 4 个子图)
# 返回 fig(整个图形对象)和 axs(子图数组,形状为 2x2)
fig, axs = plt.subplots(nrows=2, ncols=2)

# axs 是一个 2x2 的 NumPy 数组,包含四个 Axes 对象(即四个子图)
# 使用 axs.flat 可以将二维数组展平为一维迭代器,方便对每个子图进行统一操作
for ax in axs.flat:
    # 在每一个子图中绘制相同的折线图
    # x 坐标为 [1, 2, 3],y 坐标为 [4, 5, 6]
    ax.plot([1, 2, 3], [4, 5, 6])

# 显示绘制好的图形窗口
plt.show()
1.2.4 示例:绘制多个子图(约束布局)

实现代码如下所示:

# 导入 matplotlib.pyplot 模块,用于绘图
import matplotlib.pyplot as plt
# 导入 numpy 模块,使用其数值计算功能
import numpy as np

# 使用 numpy 的 linspace 函数生成一个从 0 到 10 的等差数列,共 100 个点,作为 x 轴的数据
x = np.linspace(0, 10, 100)

# 创建一个包含 2 行 2 列(共 4 个)子图的网格,并返回图形对象 fig 和子图数组 axes
# 注意:这里的 nrows=2 和 ncols=2 实际创建的是 2 行 2 列的布局,但代码注释中提到的是 2 行 3 列,可能是注释错误
fig, axes = plt.subplots(nrows=2, ncols=2, constrained_layout=True)

# 第 1 行第 1 列位置的子图绘制 sin(x) 曲线,颜色设置为蓝色
axes[0, 0].plot(x, np.sin(x), color='blue')
axes[0, 0].set_title('sin(x)')  # 设置该子图标题为 'sin(x)'

# 第 1 行第 2 列位置的子图绘制 cos(x) 曲线,颜色设置为紫色
axes[0, 1].plot(x, np.cos(x), color='purple')
axes[0, 1].set_title('cos(x)')  # 设置该子图标题为 'cos(x)'

# 第 2 行第 1 列位置的子图绘制 tan(x) 曲线,颜色设置为橙色
axes[1, 0].plot(x, np.tan(x), color='orange')
axes[1, 0].set_title('tan(x)')  # 设置该子图标题为 'tan(x)'

# 第 2 行第 2 列位置的子图绘制 exp(x/10) 曲线,颜色设置为棕色
axes[1, 1].plot(x, np.exp(x / 10), color='brown')
axes[1, 1].set_title('exp(x/10)')  # 设置该子图标题为 'exp(x/10)'

# 显示整个画布上的所有子图
plt.show()

绘制的图形如下图所示:

在这里插入图片描述

2. 紧密布局

2.1 紧密布局介绍

紧密布局(Tight Layout) 是 Matplotlib 中一种自动调整子图位置和大小的机制,旨在消除子图之间的多余空白,并确保标题、标签、刻度等元素不会被裁剪或重叠。与“约束布局”(Constrained Layout)类似,tight_layout 也是一种布局优化工具,但它采用的是后处理方式:在所有绘图完成后,自动计算并调整子图的几何位置。

2.2 启用紧密布局

2.2.1 通过 tight_layout() 启用

tight_layout() 是 Matplotlib 中最常用的自动布局优化方法之一。它会自动调整子图(Axes)的位置和间距,确保标题、坐标轴标签、刻度等元素不会被裁剪或重叠,从而生成紧凑且美观的图表。

该方法在所有绘图操作完成后调用,是一种“后处理”式的布局调整机制。

调用方式:

plt.tight_layout()
# 或
fig.tight_layout()

两者功能相同,plt.tight_layout() 作用于当前图像,fig.tight_layout() 作用于指定的 Figure 对象。

tight_layout() 方法参数说明:

参数名 类型 默认值 描述
pad float 1.08 图像边缘到子图区域的最小边距(单位:英寸)。用于防止边缘元素被裁剪。
h_pad float 或 None pad 子图之间的垂直间距(英寸)。若为 None,使用 pad 值。
w_pad float 或 None pad 子图之间的水平间距(英寸)。若为 None,使用 pad 值。
rect tuple (left, bottom, right, top) (0, 0, 1, 1) 子图占据的归一化坐标区域(0~1),常用于为 suptitle、图例等外部元素预留空间。例如:rect=[0, 0, 1, 0.95] 表示子图只占用底部 95% 的高度,顶部留出空间。
2.2.2 使用 subplots() 或 figure() 方法中的 layout 参数

Matplotlib 3.6.0 开始,引入了新的统一布局管理参数 layout,可用于 plt.subplots()plt.figure() 中,支持多种布局策略。

语法:

plt.subplots(..., layout='tight')
# 或
plt.figure(..., layout='tight')

layout 参数可选值:

描述
None 不启用自动布局(默认)。
'tight' 启用 tight_layout 自动排版。
'constrained' 启用 constrained_layout(推荐用于复杂图表)。
'compressed' 类似 'tight',但在某些情况下更紧凑(实验性)。
2.2.3 通过 figure.autolayout 配置项启用

可以通过 Matplotlib 的全局配置参数 figure.autolayout默认启用 tight_layout,这样所有后续创建的图像都会自动应用 tight_layout,无需每次手动调用。

import matplotlib.pyplot as plt
# 全局启用 tight_layout
plt.rcParams['figure.autolayout'] = True
2.2.4 示例:绘制多个子图(紧密布局)

实现代码如下所示:

# 导入 matplotlib.pyplot 模块,用于绘图
import matplotlib.pyplot as plt
# 导入 numpy 模块,使用其数值计算功能
import numpy as np

# 使用 numpy 的 linspace 函数生成一个从 0 到 10 的等差数列,共 100 个点,作为 x 轴的数据
x = np.linspace(0, 10, 100)

# 创建一个包含 2 行 2 列(共 4 个)子图的网格,并返回图形对象 fig 和子图数组 axes
# 注意:这里的 nrows=2 和 ncols=2 实际创建的是 2 行 2 列的布局,但代码注释中提到的是 2 行 3 列,可能是注释错误
fig, axes = plt.subplots(nrows=2, ncols=2)

# 第 1 行第 1 列位置的子图绘制 sin(x) 曲线,颜色设置为蓝色
axes[0, 0].plot(x, np.sin(x), color='blue')
axes[0, 0].set_title('sin(x)')  # 设置该子图标题为 'sin(x)'

# 第 1 行第 2 列位置的子图绘制 cos(x) 曲线,颜色设置为紫色
axes[0, 1].plot(x, np.cos(x), color='purple')
axes[0, 1].set_title('cos(x)')  # 设置该子图标题为 'cos(x)'

# 第 2 行第 1 列位置的子图绘制 tan(x) 曲线,颜色设置为橙色
axes[1, 0].plot(x, np.tan(x), color='orange')
axes[1, 0].set_title('tan(x)')  # 设置该子图标题为 'tan(x)'

# 第 2 行第 2 列位置的子图绘制 exp(x/10) 曲线,颜色设置为棕色
axes[1, 1].plot(x, np.exp(x / 10), color='brown')
axes[1, 1].set_title('exp(x/10)')  # 设置该子图标题为 'exp(x/10)'

# 调用 tight_layout 函数自动调整子图之间的间距,避免重叠或过于紧凑
plt.tight_layout(pad=2, h_pad=2, w_pad=2)

# 显示整个画布上的所有子图
plt.show()

绘制的图形如下图所示:

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到