如何完全移除子箱线图图例并消除残留空白?附代码优化需求
问题描述
使用matplotlib和seaborn绘制子箱线图时,执行.legend_.remove()后每个子图仍残留图例空白区域,同时希望简化现有重复代码。
原始代码:
fig, axes = plt.subplots(nrows=1, ncols=4, figsize=(18,6)) a = sns.boxplot(data = df, ax=axes[0], x='Building', y='CDL-08', hue = 'Week', palette="Set3") b = sns.boxplot(data = df, ax=axes[1], x='Building', y='CDL-09', hue = 'Week', palette="Set3") c = sns.boxplot(data = df, ax=axes[2], x='Building', y='CDL-10', hue = 'Week', palette="Set3") d = sns.boxplot(data = df, ax=axes[3], x='Building', y='CDL-11', hue = 'Week', palette="Set3") a.legend_.remove() b.legend_.remove() c.legend_.remove() d.legend_.remove() #placing legend outside plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) plt.subplots_adjust(left= 0.11, bottom=0.11, right=0.9, top=0.85, wspace=0.3, hspace=0.3) a.xaxis.label.set_visible(False) b.xaxis.label.set_visible(False) c.xaxis.label.set_visible(False) d.xaxis.label.set_visible(False) a.yaxis.label.set_visible(False) b.yaxis.label.set_visible(False) c.yaxis.label.set_visible(False) d.yaxis.label.set_visible(False) fig.text(0.5, 0.04, 'Building', ha='center', va='center', fontsize = 14) fig.text(0.06, 0.5, 'CO2 concentration (ppm)', ha='center', va='center', rotation='vertical', fontsize = 14) #threshold a.axhline(y=1000, color='red', linestyle='--', linewidth = 1.5) a.axhline(y=800, color='#8B2323', linestyle='--', linewidth = 1.5) b.axhline(y=1000, color='red', linestyle='--', linewidth = 1.5) b.axhline(y=800, color='#8B2323', linestyle='--', linewidth = 1.5) c.axhline(y=1000, color='red', linestyle='--', linewidth = 1.5) c.axhline(y=800, color='#8B2323', linestyle='--', linewidth = 1.5) d.axhline(y=1000, color='red', linestyle='--', linewidth = 1.5) d.axhline(y=800, color='#8B2323', linestyle='--', linewidth = 1.5) #common title fig.suptitle('CO2 exposures', fontsize = 18) #title for subplots a.set_title("Subject 01", fontsize=14) b.set_title("Subject 02", fontsize=14) c.set_title("Subject 03", fontsize=14) d.set_title("Subject 04", fontsize=14) plt.rcParams['xtick.labelsize'] = 14 min([], default="EMPTY") # returns EMPTY # Defining custom 'xlim' and 'ylim' values. custom_ylim = (300, 1500) # Setting the values for all axes. plt.setp(axes, ylim=custom_ylim, yticks=[300, 600, 900, 1200, 1500]) a.set_yticklabels([300, 600, 900, 1200, 1500], fontsize=15) b.set_yticklabels([300, 600, 900, 1200, 1500], fontsize=15) c.set_yticklabels([300, 600, 900, 1200, 1500], fontsize=15) d.set_yticklabels([300, 600, 900, 1200, 1500], fontsize=15) plt.show()
附图:
问题原因
- 使用
sns.boxplot时指定hue参数,seaborn会自动为每个子图预留图例布局空间,仅调用.legend_.remove()只能删除图例元素,无法收回预留的空白区域。 - 后续执行
plt.legend()创建全局图例,但未解决子图预留空间的问题,导致空白依然存在。 - 原始代码存在大量重复操作,如重复绘制水平线、重复设置轴标签可见性等,代码冗余且不易维护。
解决方案与简化代码
核心优化措施
- 绘制箱线图时直接通过
legend=False禁用图例创建,从根源避免子图预留空白空间 - 采用循环批量处理所有子图,消除重复代码
- 提取公共配置(如水平线、轴范围、刻度样式)统一设置
- 基于子图的图例手柄和标签创建全局图例,保证样式与箱线图一致
简化后的代码:
import matplotlib.pyplot as plt import seaborn as sns # 初始化画布 fig, axes = plt.subplots(nrows=1, ncols=4, figsize=(18, 6)) # 定义子图相关配置参数 y_columns = ['CDL-08', 'CDL-09', 'CDL-10', 'CDL-11'] subplot_titles = ["Subject 01", "Subject 02", "Subject 03", "Subject 04"] custom_ylim = (300, 1500) ytick_values = [300, 600, 900, 1200, 1500] # 循环批量绘制子图 for ax, y_col, title in zip(axes, y_columns, subplot_titles): # 绘制箱线图时禁用图例,避免预留空间 sns.boxplot(data=df, ax=ax, x='Building', y=y_col, hue='Week', palette="Set3", legend=False) # 添加阈值水平线 ax.axhline(y=1000, color='red', linestyle='--', linewidth=1.5) ax.axhline(y=800, color='#8B2323', linestyle='--', linewidth=1.5) # 设置子图标题 ax.set_title(title, fontsize=14) # 隐藏子图自身的轴标签 ax.set_xlabel('') ax.set_ylabel('') # 统一设置y轴范围和刻度 ax.set_ylim(custom_ylim) ax.set_yticks(ytick_values) ax.set_yticklabels(ytick_values, fontsize=15) # 创建全局图例(基于第一个子图的图例数据,保证样式匹配) handles, labels = axes[0].get_legend_handles_labels() fig.legend(handles, labels, bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) # 设置全局标题和轴标签 fig.suptitle('CO2 exposures', fontsize=18) fig.text(0.5, 0.04, 'Building', ha='center', va='center', fontsize=14) fig.text(0.06, 0.5, 'CO2 concentration (ppm)', ha='center', va='center', rotation='vertical', fontsize=14) # 调整全局样式和子图间距 plt.rcParams['xtick.labelsize'] = 14 plt.subplots_adjust(left=0.11, bottom=0.11, right=0.85, top=0.85, wspace=0.3) plt.show()
效果说明
- 彻底消除子图中的图例空白区域,布局更紧凑合理
- 代码结构清晰,重复操作全部通过循环批量处理,便于后续修改和扩展
- 全局图例样式与箱线图保持一致,视觉效果统一
内容的提问来源于stack exchange,提问作者Yunni
相关产品推荐
相关产品推荐

