如何将图例相对于GridSpec单元格对齐,为子图组配置独立图例
解决方案
核心思路是给图例专门预留网格位置,避免手动调整坐标,同时不挤压原有子图的显示空间:
- 调整外层GridSpec结构,新增一列专门用于放置两个独立图例,通过宽度比例控制图例区域的宽度
- 通过GridSpec自带的坐标获取方法拿到对应图例区域的边界,作为图例的对齐基准,无需手动计算锚点坐标
- 图例对齐直接用
loc参数即可自动适配预留区域,适配性强,画布尺寸修改后也不需要重新调整位置
修改后的可运行代码
#!/usr/bin/env python3 import pandas as pd, seaborn as sns import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec GENOMES = ["spneumoniae", "ecoliK12", "scerevisiae", "celegans", "bmori", "hg38"] fig = plt.figure(figsize = (7, 8)) # 修改外层网格:2行2列,新增右侧列放图例,宽度比例8.8:1.2,高度比例保持2:1 outer_grid = gridspec.GridSpec(2, 2, width_ratios=[0.88, 0.12], height_ratios = [2, 1]) # 原有子图网格都放在左侧第一列 inner_grid1 = gridspec.GridSpecFromSubplotSpec(4, 3, subplot_spec=outer_grid[0, 0]) inner_grid2 = gridspec.GridSpecFromSubplotSpec(2, 3, subplot_spec=outer_grid[1, 0]) # 先绘制所有子图 for index, genome in enumerate(GENOMES): data = pd.DataFrame({"x": [0, 1, 2, 3, 0, 1, 2, 3], "y": [1, 0, 3, 2, 1, 0, 3, 2], "hue": ["a", "a", "a", "a", "b", "b", "b", "b"]}) # 第一组6幅子图 ax1 = plt.Subplot(fig, inner_grid1[index]) ax1 = sns.lineplot(data = data, x = "x", y = "y", hue = "hue", ax = ax1, legend=False) ax1.set_xlabel("") ax1.set_ylabel("") ax1.legend([], [], loc = "lower center", title = f"{genome}") fig.add_subplot(ax1) # 第二组6幅子图 ax2 = plt.Subplot(fig, inner_grid1[index + 6]) ax2 = sns.lineplot(data = data, x = "x", y = "y", hue = "hue", ax = ax2, legend=False) ax2.set_xlabel("") ax2.set_ylabel("") ax2.legend([], [], loc = "upper center", title = f"{genome}") fig.add_subplot(ax2) # 第三组6幅子图 ax3 = plt.Subplot(fig, inner_grid2[index]) ax3 = sns.lineplot(data = data, x = "x", y = "y", hue = "hue", ax = ax3, legend=False) ax3.set_xlabel("") ax3.set_ylabel("") ax3.legend([], [], loc = "upper center", title = f"{genome}") fig.add_subplot(ax3) # 单独生成两个图例,自动对齐到右侧预留区域 # 获取inner_grid1对应的右侧图例区域坐标 legend1_box = outer_grid[0,1].get_position(fig) handles1, labels1 = ax1.get_legend_handles_labels() fig.legend(handles1, labels1, loc="center left", bbox_to_anchor=legend1_box, bbox_transform=fig.transFigure) # 获取inner_grid2对应的右侧图例区域坐标 legend2_box = outer_grid[1,1].get_position(fig) handles2, labels2 = ax3.get_legend_handles_labels() # 如需自定义标签直接替换labels2即可,比如labels2 = ["#unitigs", "avg. unitig len."] fig.legend(handles2, labels2, loc="center left", bbox_to_anchor=legend2_box, bbox_transform=fig.transFigure) plt.savefig("stackoverflow_test.pdf", bbox_inches="tight")
关键修改说明
- 预留图例位置:外层GridSpec新增右侧列,子图和图例区域完全分离,不会出现图例挤压子图缩放的问题
- 自动获取对齐基准:通过
get_position方法直接拿到对应网格区域的坐标,无需手动计算bbox_to_anchor的数值,适配性极强 - 对齐逻辑简化:
loc参数会自动基于预留的图例区域做对齐,比如center left就是对齐到预留区域的左侧中间位置,符合直觉不需要反复调试
内容的提问来源于stack exchange,提问作者Sebastian Schmidt
相关产品推荐
相关产品推荐

