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

Seaborn FacetGrid:在最后一个子图后添加单个颜色条

解决Seaborn子图网格添加单个颜色条且不改变子图尺寸的问题

哈哈,这个问题我之前做Seaborn分面图的时候也踩过坑——要么每个子图都带颜色条显得冗余,要么硬塞一个把第三个子图挤变形,太闹心了!给你两个亲测有效的方案,直接套进你的代码里就能解决:

方案一:用Matplotlib GridSpec预留颜色条位置(最稳妥)

这个方法的核心是提前给颜色条单独划分一块画布区域,完全不占用子图的空间,能保证三个子图尺寸完全一致。

代码示例

import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

def masked_vs_unmasked_facets(output_dir, merged_df, target_col, thresholds):
    # 设置画布大小,根据你的需求调整
    fig = plt.figure(figsize=(16, 5))
    
    # 创建GridSpec:1行4列,前3列是子图,第4列是颜色条(宽度占比0.12,可按需调整)
    gs = GridSpec(1, 4, width_ratios=[1, 1, 1, 0.12])
    
    # 初始化三个子图和颜色条的轴
    ax1 = fig.add_subplot(gs[0, 0])
    ax2 = fig.add_subplot(gs[0, 1])
    ax3 = fig.add_subplot(gs[0, 2])
    cbar_ax = fig.add_subplot(gs[0, 3])
    
    # 统一所有子图的颜色映射范围(关键!否则颜色条和子图不匹配)
    vmin = merged_df[target_col].min()
    vmax = merged_df[target_col].max()
    cmap = "viridis"  # 替换成你需要的颜色映射
    
    # 绘制每个子图,注意关闭单独的颜色条(cbar=False)
    # 这里以热力图为例,你可以换成scatterplot、kdeplot等其他Seaborn图表
    sns.heatmap(merged_df[merged_df["threshold"] == thresholds[0]],
                ax=ax1, vmin=vmin, vmax=vmax, cmap=cmap, cbar=False)
    sns.heatmap(merged_df[merged_df["threshold"] == thresholds[1]],
                ax=ax2, vmin=vmin, vmax=vmax, cmap=cmap, cbar=False)
    sns.heatmap(merged_df[merged_df["threshold"] == thresholds[2]],
                ax=ax3, vmin=vmin, vmax=vmax, cmap=cmap, cbar=False)
    
    # 把颜色条添加到预留的轴上
    fig.colorbar(ax3.collections[0], cax=cbar_ax)
    
    # 调整子图间距,避免标签重叠
    plt.tight_layout()
    # 保存图片时加上bbox_inches="tight",防止颜色条被裁剪
    plt.savefig(f"{output_dir}/facets_plot.png", bbox_inches="tight")
    plt.close()

关键要点

  • width_ratios参数控制子图和颜色条的宽度比例,0.12这个数值可以根据颜色条的长度调整,保证视觉协调。
  • 必须统一所有子图的vmin、vmax和cmap,这样颜色条的刻度才能对应所有子图的数值范围。
  • 每个子图绘制时务必设置cbar=False,避免生成单个子图的颜色条。

方案二:用Seaborn FacetGrid(适合分面场景)

如果你的数据本身是按thresholds分组的,用FacetGrid来生成子图会更简洁,同样能实现统一颜色条的效果。

代码示例

import seaborn as sns
import matplotlib.pyplot as plt

def masked_vs_unmasked_facets(output_dir, merged_df, target_col, thresholds):
    # 筛选出需要的阈值数据,初始化FacetGrid(col_wrap=3表示一行放3个子图)
    g = sns.FacetGrid(merged_df[merged_df["threshold"].isin(thresholds)],
                      col="threshold", col_wrap=3, height=5)
    
    # 定义统一的绘图函数,保证所有子图颜色映射一致
    def plot_subplot(data, **kwargs):
        sns.heatmap(data.pivot_table(index="xxx", columns="yyy", values=target_col),  # 替换成你的数据维度
                    vmin=merged_df[target_col].min(), vmax=merged_df[target_col].max(),
                    cmap="viridis", cbar=False, **kwargs)
    
    # 把绘图函数映射到每个分面子图
    g.map_dataframe(plot_subplot)
    
    # 给颜色条预留空间,然后添加颜色条
    g.figure.subplots_adjust(right=0.85)  # 调整右侧空间占比
    # 手动指定颜色条的位置:[左, 下, 宽, 高],数值可按需微调
    cbar_ax = g.figure.add_axes([0.88, 0.15, 0.02, 0.7])
    g.figure.colorbar(g.axes[0].collections[0], cax=cbar_ax)
    
    plt.savefig(f"{output_dir}/facets_plot.png", bbox_inches="tight")
    plt.close()

关键要点

  • 如果你的子图是散点图等集合类型,把g.axes[0].collections[0]换成对应的颜色对象即可(比如散点图的g.axes[0].patches或者g.axes[0].collections[0])。
  • add_axes的参数需要根据你的画布大小微调,保证颜色条和子图垂直对齐。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:19:17