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
相关产品推荐
相关产品推荐

