如何在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
相关产品推荐
相关产品推荐

