如何绘制不等列数、共享X轴的多子图?解决布局与图例难题
非规则网格子图布局与图例优化方案
一、非规则子图布局解决方案
针对每行列数不同、tight_layout报错的问题,推荐以下自动布局方案:
1. 正确启用constrained_layout
之前启用无效是因为时机错误,需在创建figure时直接指定参数,它支持非规则网格的自动间距调整:
fig = plt.figure(figsize=choose_size, clear=True, constrained_layout=True)
该参数会自动适配子图间距、标题位置,避免元素重叠。
2. 用subplot_mosaic定义非规则布局
如果子图结构固定,subplot_mosaic可直观定义每行列数,完美适配非规则排列:
# 示例:2行布局,第一行2个子图,第二行1个子图 fig, axs = plt.subplot_mosaic([ ["row1_col1", "row1_col2"], ["row2_col1"] ], figsize=choose_size, constrained_layout=True) # 对应子图绘图 axs["row1_col1"].plot(lvl_1[x_val], lvl_1[...]) axs["row1_col1"].set_title("xxx") # 其他子图同理
3. 动态计算subplots_adjust参数
若坚持原有add_subplot逻辑,可根据行数、列数动态计算间距,替代手动调整:
n_rows = len(v1.keys()) max_cols = max(len(v2) for v2 in v1.values()) # 获取最大列数 fig.subplots_adjust( top=0.85, # 给suptitle预留空间 bottom=0.1, left=0.07, right=0.93, hspace=0.3 + 0.1*(n_rows>3), # 行数多则增大行间距 wspace=0.2 + 0.1*(max_cols>2) # 列数多则增大列间距 )
4. 实现X轴共享
所有子图共享X轴,减少重复标签、节省空间:
first_ax = None # 保存第一个子图作为共享基准 # 在创建子图的循环中添加: ax = fig.add_subplot(...) if first_ax is None: first_ax = ax else: ax.sharex(first_ax) # 绑定X轴共享 # 隐藏非底部子图的X轴标签 for ax in fig.axes: # 根据布局逻辑判断是否为最后一行 if ax.get_subplotspec().rowspan.start != n_rows - 1: ax.set_xticklabels([])
二、图例优化方案
针对子图空间不足的问题,推荐以下清晰图例生成方式:
1. 全局统一图例
收集所有子图的图例项,在画布外侧单独放置:
# 收集唯一的图例手柄和标签 all_handles = [] all_labels = [] for ax in fig.axes: handles, labels = ax.get_legend_handles_labels() for h, l in zip(handles, labels): if l not in all_labels: all_handles.append(h) all_labels.append(l) # 在画布右侧添加全局图例 fig.legend( all_handles, all_labels, loc="center right", bbox_to_anchor=(1.2, 0.5), # 调整位置避免超出画布 fontsize=10 )
2. 单独生成图例图片
若主图完全无空间,可单独导出图例文件:
# 创建仅含图例的画布 fig_legend = plt.figure(figsize=(2.5, 5)) fig_legend.legend(all_handles, all_labels, loc="center") # 保存图例 legend_path = os.path.join(folder_results_path, f"{k1}_legend.png") fig_legend.savefig(legend_path, bbox_inches="tight") plt.close(fig_legend)
3. 精简图例标签
若标签过长,可截断或简化:
# 示例:将标签截断至15字符以内 short_label = _finally[:15] + "..." if len(_finally) >15 else _finally ax.plot(lvl_1[x_val], lvl_1[_finally], label=short_label)
内容的提问来源于stack exchange,提问作者Gregorio Casagrande
相关产品推荐
相关产品推荐

