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

Seaborn displot:如何使common_norm仅作用于FacetGrid子图而非hue?

解决Seaborn displot子图内百分比总和为100%的问题

问题背景

使用Seaborn 0.12.0的displot绘制带hue分类的FacetGrid百分比直方图时,现有参数无法满足需求:

  • 设置common_norm=False:子图内的hue类别会各自归一,导致整个子图的bins总和超过100%
  • 设置common_norm=True:所有子图的整体数据归一,单个子图总和不是100%

需要实现**每个子图内所有bins(含所有hue类别)总和为100%**的效果。

解决方案:手动控制子图层面的归一化

可以通过FacetGrid结合自定义绘制逻辑,单独对每个子图内的所有数据计算百分比,确保子图内总和为100%。以下是两种可行方案:

方案一:自定义直方图计算逻辑

import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

tips = sns.load_dataset("tips")

# 初始化FacetGrid布局
g = sns.FacetGrid(tips, col="smoker", row="time", margin_titles=True, sharey="row")

# 定义子图绘制函数
def plot_percent_hist(data, x, hue, ax=None):
    total = len(data)
    # 统一所有子图的bins范围
    bins = np.histogram_bin_edges(tips[x], bins='auto')
    # 按hue分组计算百分比
    for name, group in data.groupby(hue)[x]:
        counts, _ = np.histogram(group, bins=bins)
        percents = counts / total * 100
        ax.stairs(percents, bins, fill=True, label=name, alpha=0.7)
    ax.set_ylabel("Percent")
    ax.legend(title=hue)
    ax.set_xlabel(x)

# 将绘制逻辑映射到每个子图
g.map_dataframe(plot_percent_hist, x="tip", hue="sex")

plt.tight_layout()
plt.show()

方案二:利用权重参数简化实现

借助histplot的weights参数,直接为子图内的每个样本设置权重,确保总和归一为100%:

import seaborn as sns
import matplotlib.pyplot as plt

tips = sns.load_dataset("tips")

g = sns.FacetGrid(tips, col="smoker", row="time", margin_titles=True, sharey="row")

def plot_weighted_hist(data, x, hue, ax=None):
    # 计算当前子图的总数据量,生成权重
    data['weight'] = 100 / len(data)
    sns.histplot(data=data, x=x, hue=hue, weights='weight', bins='auto', stat='count', ax=ax)
    ax.set_ylabel("Percent")

g.map_dataframe(plot_weighted_hist, x="tip", hue="sex")

plt.tight_layout()
plt.show()

两种方案都能保证每个子图内所有hue类别的bins总和为100%,同时保留FacetGrid的布局和hue分类的可视化效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 14:50:38