当前位置:首页>python>一起学Python第109天:Matplotlib 多子图布局

一起学Python第109天:Matplotlib 多子图布局

  • 2026-10-10 16:29:23
一起学Python第109天:Matplotlib 多子图布局

一、为什么需要多子图?

想象一下:你要做一份销售分析报告,需要同时展示趋势图、占比图、对比图、分布图……如果每张图单独保存,读者得来回切换,体验极差。

多子图(Subplot) 就是解决方案——把多个图表像拼图一样拼进一张大图里,一屏看完所有关键信息。

二、两种核心方法:subplot() vs subplots()

💡 推荐:新手先用 subplot() 理解原理,实际项目用 subplots() 更高效。

三、subplot():逐个定位法

核心语法

plt.subplot(nrows, ncols, index)# 或简写:plt.subplot(行数, 列数, 位置编号)
编号规则:从左到右、从上到下,从 1 开始计数。
1x2 布局:          2x2 布局:┌─────┬─────┐     ┌─────┬─────┐│  1  │  2  │     │  1  │  2  │└─────┴─────┘     ├─────┼─────┤                    │  3  │  4  │                    └─────┴─────┘

示例 1:1行2列并排对比

import numpy as npimport matplotlib.pyplot as pltimport matplotlib.font_manager as fmimport os# ================= 1. 字体加载(解决中文乱码的核心) =================font_path = "simhei.ttf"  # 确保当前目录有 simhei.ttfif not os.path.exists(font_path):    font_path = "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc"  # 备用系统字体prop = fm.FontProperties(fname=font_path)plt.rcParams['axes.unicode_minus'] = False# 数据准备x = np.array([0, 6])y1 = np.array([0, 100])x2 = np.array([1, 2, 3, 4])y2 = np.array([1, 4, 9, 16])# 子图1:位置 (1, 2, 1) = 第1行第2列的第1个plt.subplot(1, 2, 1)plt.plot(x, y1)plt.title("线性增长", fontproperties=prop)# 子图2:位置 (1, 2, 2) = 第1行第2列的第2个plt.subplot(1, 2, 2)plt.plot(x2, y2)plt.title("指数增长", fontproperties=prop)# 总标题plt.suptitle("增长模式对比", fontproperties=prop, fontsize=16)plt.tight_layout()  # 自动调整间距plt.show()

示例 2:2行2列四宫格

# 数据x = np.array([1, 2, 3, 4])# 子图1:左上plt.subplot(2, 2, 1)plt.plot(x, x)plt.title("y = x")# 子图2:右上plt.subplot(2, 2, 2)plt.plot(x, x**2)plt.title("y = x²")# 子图3:左下plt.subplot(2, 2, 3)plt.plot(x, x**3)plt.title("y = x³")# 子图4:右下plt.subplot(2, 2, 4)plt.plot(x, np.sqrt(x))plt.title("y = √x")plt.suptitle("TEST109-1", fontsize=16)plt.tight_layout()plt.show()

四、subplots():批量创建法(推荐)

核心语法

fig, axes = plt.subplots(nrows=1, ncols=1, sharex=False, sharey=False)
参数
说明
常用值
nrows
行数
1, 2, 3...
ncols
列数
1, 2, 3...
sharex
共享X轴
False
, True, 'col', 'row'
sharey
共享Y轴
False
, True, 'col', 'row'
figsize
画布尺寸
(10, 8)
dpi
分辨率
100, 200, 300

示例 3:基础用法

import numpy as npimport matplotlib.pyplot as pltimport matplotlib.font_manager as fmimport os# ================= 1. 字体加载 =================font_path = "simhei.ttf"if not os.path.exists(font_path):    font_path = "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc"prop = fm.FontProperties(fname=font_path)plt.rcParams['axes.unicode_minus'] = False# 数据x = np.linspace(0, 2*np.pi, 400)y = np.sin(x**2)# 创建 1 个子图fig, ax = plt.subplots(figsize=(8, 5))ax.plot(x, y)ax.set_title('简单曲线', fontproperties=prop)plt.show()

示例 4:1行2列 + 共享Y轴

import numpy as npimport matplotlib.pyplot as pltimport matplotlib.font_manager as fmimport os# ================= 1. 字体加载(解决中文乱码的核心) =================font_path = "simhei.ttf"  # 确保你已经在当前目录上传了 simhei.ttfif not os.path.exists(font_path):    font_path = "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc"  # 备用系统字体prop = fm.FontProperties(fname=font_path)plt.rcParams['axes.unicode_minus'] = False# ================= 2. 数据准备 =================x = np.linspace(0, 2 * np.pi, 400)  # 0 到 2π,400个点y = np.sin(x ** 2)                   # y = sin(x²),产生漂亮的振荡曲线# ================= 3. 创建 1行2列 子图,共享Y轴 =================fig, (ax1, ax2) = plt.subplots(1, 2, sharey=True, figsize=(12, 5))# ---------- 左图:折线图 ----------ax1.plot(x, y, color='#2E86AB', linewidth=1.5)ax1.set_title('折线图', fontproperties=prop, fontsize=14)ax1.set_xlabel('X 轴(弧度)', fontproperties=prop)ax1.set_ylabel('Y 值', fontproperties=prop)ax1.grid(True, alpha=0.3, linestyle='--')# ---------- 右图:散点图(共享Y轴,刻度对齐) ----------# 为了散点图效果更明显,每隔10个点取一个x_sample = x[::10]y_sample = y[::10]ax2.scatter(x_sample, y_sample, c='#E74C3C', s=20, alpha=0.6, edgecolors='white')ax2.set_title('散点图', fontproperties=prop, fontsize=14)ax2.set_xlabel('X 轴(弧度)', fontproperties=prop)ax2.grid(True, alpha=0.3, linestyle='--')# ================= 4. 总标题与布局 =================plt.suptitle('同一数据的不同展示', fontproperties=prop, fontsize=16, y=1.02)# tight_layout 在 suptitle 之后调用,避免总标题被覆盖plt.tight_layout(rect=[0, 0, 1, 0.98])  # 为总标题留出顶部空间plt.show()
🔥 共享轴的好处:Y轴刻度统一,便于直接对比数值差异。

五、共享轴的4种模式详解

模式
说明
效果
sharex=False
不共享(默认)
每个子图X轴独立
sharex='all'
 / True
全部共享
所有子图X轴统一
sharex='col'
按列共享
同一列的子图X轴相同
sharex='row'
按行共享
同一行的子图X轴相同
import numpy as npimport matplotlib.pyplot as pltimport matplotlib.font_manager as fmimport os# ================= 1. 字体加载(解决中文乱码的核心) =================font_path = "simhei.ttf"  # 确保你已经在当前目录上传了 simhei.ttfif not os.path.exists(font_path):    font_path = "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc"  # 备用系统字体prop = fm.FontProperties(fname=font_path)plt.rcParams['axes.unicode_minus'] = False# ================= 2. 数据准备 =================x = np.linspace(0, 2 * np.pi, 400)  # 0 到 2π,400个点# ================= 3. 创建 2x2 布局,按列共享X轴,按行共享Y轴 =================fig, axes = plt.subplots(2, 2, sharex='col', sharey='row', figsize=(10, 8))# ---------- 左上:sin(x) ----------axes[0, 0].plot(x, np.sin(x), color='#2E86AB', linewidth=2)axes[0, 0].set_title('正弦函数 sin(x)', fontproperties=prop, fontsize=12)axes[0, 0].set_ylabel('Y 值', fontproperties=prop)axes[0, 0].grid(True, alpha=0.3, linestyle='--')# ---------- 右上:cos(x) ----------axes[0, 1].plot(x, np.cos(x), color='#E74C3C', linewidth=2)axes[0, 1].set_title('余弦函数 cos(x)', fontproperties=prop, fontsize=12)axes[0, 1].grid(True, alpha=0.3, linestyle='--')# 注意:由于 sharey='row',右上与左上共享Y轴,右侧Y轴刻度自动隐藏# ---------- 左下:tan(x)(限制范围避免无穷大)----------# tan(x) 在 π/2, 3π/2 处有渐近线,需要截断y_tan = np.tan(x)y_tan[np.abs(y_tan) > 10] = np.nan  # 超过10的值设为NaN,避免画出垂直线axes[1, 0].plot(x, y_tan, color='#27AE60', linewidth=2)axes[1, 0].set_title('正切函数 tan(x)', fontproperties=prop, fontsize=12)axes[1, 0].set_xlabel('X 轴(弧度)', fontproperties=prop)axes[1, 0].set_ylabel('Y 值', fontproperties=prop)axes[1, 0].set_ylim(-10, 10)  # 限制Y轴范围axes[1, 0].grid(True, alpha=0.3, linestyle='--')# ---------- 右下:exp(x) ----------y_exp = np.exp(x)axes[1, 1].plot(x, y_exp, color='#9B59B6', linewidth=2)axes[1, 1].set_title('指数函数 exp(x)', fontproperties=prop, fontsize=12)axes[1, 1].set_xlabel('X 轴(弧度)', fontproperties=prop)axes[1, 1].grid(True, alpha=0.3, linestyle='--')# 注意:由于 sharex='col',右下与左下共享X轴,底部X轴刻度已显示#       由于 sharey='row',右下与右上共享Y轴?不对!#       sharey='row' 表示:同一行的子图共享Y轴#       所以 左上和右上 共享Y轴,左下和右下 共享Y轴# ================= 4. 总标题与布局 =================plt.suptitle('2x2 共享轴布局 - 经典函数对比', fontproperties=prop, fontsize=16, y=1.02)# tight_layout 在 suptitle 之后调用plt.tight_layout(rect=[0, 0, 1, 0.98])plt.show()

六、实战:一份完整的数据分析看板

import numpy as npimport matplotlib.pyplot as pltimport matplotlib.font_manager as fmimport os# ================= 1. 字体加载 =================font_path = "simhei.ttf"if not os.path.exists(font_path):    font_path = "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc"prop = fm.FontProperties(fname=font_path)plt.rcParams['axes.unicode_minus'] = False# 数据months = ['1月', '2月', '3月', '4月', '5月', '6月']sales = [120, 135, 148, 162, 155, 178]profit = [20, 25, 30, 35, 28, 40]categories = ['A', 'B', 'C', 'D', 'E']values = [25, 40, 30, 55, 15]# 创建 2x2 布局fig, axes = plt.subplots(2, 2, figsize=(14, 10))# ========== 子图1:销售趋势(折线图)==========ax1 = axes[0, 0]ax1.plot(months, sales, marker='o', color='#2E86AB', linewidth=2)ax1.set_title('月度销售额趋势', fontproperties=prop, fontsize=12)ax1.set_ylabel('销售额(万元)', fontproperties=prop)ax1.grid(True, alpha=0.3)# ========== 子图2:利润趋势(柱状图)==========ax2 = axes[0, 1]bars = ax2.bar(months, profit, color='#E74C3C', alpha=0.7)ax2.set_title('月度利润', fontproperties=prop, fontsize=12)ax2.set_ylabel('利润(万元)', fontproperties=prop)for bar in bars:    height = bar.get_height()    ax2.text(bar.get_x() + bar.get_width()/2., height + 1,             f'{height}', ha='center', fontsize=9)# ========== 子图3:品类占比(饼图)==========ax3 = axes[1, 0]colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8']ax3.pie(values, labels=categories, colors=colors, autopct='%1.1f%%',        startangle=90, textprops={'fontproperties': prop})ax3.set_title('品类占比', fontproperties=prop, fontsize=12)# ========== 子图4:销售vs利润(散点图)==========ax4 = axes[1, 1]ax4.scatter(sales, profit, s=200, c='#9B59B6', alpha=0.7, edgecolors='white')ax4.set_title('销售额 vs 利润', fontproperties=prop, fontsize=12)ax4.set_xlabel('销售额', fontproperties=prop)ax4.set_ylabel('利润', fontproperties=prop)ax4.grid(True, alpha=0.3)# 总标题fig.suptitle('2026年上半年经营分析看板', fontproperties=prop, fontsize=18, y=0.98)plt.tight_layout(rect=[0, 0, 1, 0.96])  # 为总标题留出空间plt.show()

参数速查卡

# subplot 快速定位plt.subplot(2, 2, 1)  # 2行2列第1个# subplots 批量创建fig, axes = plt.subplots(2, 2, sharex=True, sharey=True, figsize=(10, 8))# 访问子图axes[0, 0].plot(...)  # 第一行第一列axes[1, 1].scatter(...)  # 第二行第二列# 调整布局plt.tight_layout()  # 自动紧凑plt.subplots_adjust(hspace=0.3, wspace=0.3)  # 手动调间距

十、今日练习

  1. 基础题:用 subplot() 创建 1行3列 的并排子图

  2. 进阶题:用 subplots() 创建 2x2 布局,并共享X轴

  3. 挑战题:制作一份包含折线、柱状、饼图、散点的数据分析看板

十一、总结

今天我们掌握了 Matplotlib 多子图的核心技能:

  • ✅ plt.subplot() —— 逐个定位,适合简单场景

  • ✅ plt.subplots() —— 批量创建,面向对象,推荐日常使用

  • ✅ sharex / sharey —— 共享轴,让对比更直观

  • ✅ plt.tight_layout() —— 自动调整,告别重叠

  • ✅ 字体加载 —— FontProperties 加载 simhei.ttf,中文显示无忧

🎯 今日金句:单图是数据的独白,多子图是数据的交响——学会布局,才能让数据讲出完整的故事。

最新文章

随机文章