You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Matplotlib的2列子图布局中将图例放置在绘图区域外?

解决Matplotlib多列子图底部图例导致列间距异常的问题

直接用plt.legend()搭配fig.transFigure时,tight_layout会自动调整子图布局适配图例,导致列间出现多余空白。以下是两种实用修正方案:

方案一:用fig.legend()配合tight_layout预留空间

改用画布层面的fig.legend(),同时通过tight_layout的rect参数给底部图例预留区域,既能保持子图默认列间距,又能让图例居中放在底部。

修正后的完整可运行代码:

import numpy as np
import matplotlib.pyplot as plt

# 补充原代码缺失的变量
gp_color = '#1f77b4'
mf_color = '#ff7f0e'
mo_color = '#2ca02c'
textfs = 12

r2x = np.arange(1,10,1)
rmx = np.arange(1,10,1)

a0_r2_gp = np.random.rand(9)
a0_r2_mf = np.random.rand(9)
a0_r2_mo = np.random.rand(9)

a0_rmse_gp = np.random.randint(0, 60, 9)
a0_rmse_mf = np.random.randint(0, 50, 9)
a0_rmse_mo = np.random.randint(0, 40, 9)

a1_r2_gp = np.random.rand(9)
a1_r2_mf = np.random.rand(9)
a1_r2_mo = np.random.rand(9)

a1_rmse_gp = np.random.randint(0, 30, 9)
a1_rmse_mf = np.random.randint(0, 20, 9)
a1_rmse_mo = np.random.randint(0, 15, 9)

fig, axs = plt.subplots(2, 2, figsize=(8, 12) )
axs[0,0].plot(r2x, np.asarray(a0_r2_gp), marker='o', c = gp_color, label='GP')
axs[0,0].plot(r2x, np.asarray(a0_r2_mf), marker='o', c = mf_color, label='MF')
axs[0,0].plot(r2x, np.asarray(a0_r2_mo), marker='o', c = mo_color, label='MO')
axs[0,0].set_xticks(r2x)
axs[0,0].set_yticks(np.linspace(0,1,11))
axs[0,0].set_ylim(0, 1)
axs[0,0].set_ylabel("r², 1st case", fontsize=textfs)

axs[0,1].plot(rmx, np.asarray(a0_rmse_gp), marker='o', c = gp_color, label='GP')
axs[0,1].plot(rmx, np.asarray(a0_rmse_mf), marker='o', c = mf_color, label='MF')
axs[0,1].plot(rmx, np.asarray(a0_rmse_mo), marker='o', c = mo_color, label='MO')
axs[0,1].set_yticks(np.arange(10, 65, 5))
axs[0,1].set_xticks(rmx)
axs[0,1].set_ylim(10, 65)
axs[0,1].set_ylabel("%rmse", fontsize=textfs)
fig.suptitle("r² and %rmse averages for Tubes", fontsize=textfs)

axs[1,0].plot(r2x, np.asarray(a1_r2_gp), marker='o', c = gp_color, label='GP')
axs[1,0].plot(r2x, np.asarray(a1_r2_mf), marker='o', c = mf_color, label='MF')
axs[1,0].plot(r2x, np.asarray(a1_r2_mo), marker='o', c = mo_color, label='MO')
axs[1,0].set_xticks(r2x)
axs[1,0].set_yticks(np.linspace(0,1,11))
axs[1,0].set_ylim(0, 1)
axs[1,0].set_ylabel("r², 2nd case", fontsize=textfs)

axs[1,1].plot(rmx, np.asarray(a1_rmse_gp), marker='o', c = gp_color, label='GP')
axs[1,1].plot(rmx, np.asarray(a1_rmse_mf), marker='o', c = mf_color, label='MF')
axs[1,1].plot(rmx, np.asarray(a1_rmse_mo), marker='o', c = mo_color, label='MO')
axs[1,1].set_yticks(np.arange(6, 30, 2))
axs[1,1].set_xticks(rmx)
axs[1,1].set_ylim(6, 30)
axs[1,1].set_ylabel("%rmse", fontsize=textfs)

# 替换原图例代码,复用第一个子图的图例手柄和标签
handles, labels = axs[0,0].get_legend_handles_labels()
fig.legend(handles=handles, labels=labels,
           loc="lower center", 
           bbox_to_anchor=(0.5, 0.02),  # 微调y轴位置避免贴边
           ncol=3)
# 用rect参数预留底部图例空间,rect=(左, 上, 宽, 高),压缩底部区域给图例
plt.tight_layout(rect=[0, 0.1, 1, 0.9])
plt.show()

方案二:手动设置子图间距,跳过tight_layout干扰

如果不想依赖tight_layout,可以手动定义子图的间距参数,再直接放置图例:

# 子图绘制代码同前...

# 手动设置子图间距(wspace是列间距,保持默认值即可)
plt.subplots_adjust(left=0.1, right=0.9, bottom=0.1, top=0.9, wspace=0.2, hspace=0.3)
# 添加图例
handles, labels = axs[0,0].get_legend_handles_labels()
fig.legend(handles=handles, labels=labels,
           loc="lower center",
           bbox_to_anchor=(0.5, 0.03),
           ncol=3)
plt.show()

核心要点

  • fig.legend()是画布级别的图例,不会触发子图布局的自动调整,能保留列间默认间距;
  • 只需要从任意一个子图提取图例的手柄和标签即可,无需重复在所有子图中定义;
  • tight_layout的rect参数可以精准划定子图的显示区域,避免图例挤压子图;
  • 微调bbox_to_anchor的y值,能让图例和子图保持合适的距离,不会贴得太近或太远。

内容的提问来源于stack exchange,提问作者theprogressor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 14:54:50