如何绘制两个不重叠的100%堆叠条形图?Seaborn使用问题
解决堆叠条形图重叠及异常图形问题
问题根源
- 原代码使用累计百分比(
percent_cum)绘制,导致后续条形覆盖前序条形;指定y参数后,每个条形都从0延伸到累计值,最终出现块状异常。 - 拆分多个小数据框绘制,既繁琐又容易出现轴对齐偏差。
- 未指定统一的
ax参数,默认每次创建新坐标轴,直接导致图形重叠。
修正步骤
1. 调整数据结构:计算相对百分比(而非累计)
堆叠条形图需要每个类别单独的占比,而非累计值。先转换数据格式,再合并两个数据集并增加分组标识:
import pandas as pd import seaborn as sns import matplotlib.pyplot as plt # 计算每个类别的相对占比(从累计值转换) dfInd = pd.DataFrame({ 'Winner': ['Dog', 'Cat', 'Neither'], 'percent': [13, 48-13, 100-48], # Dog:13, Cat:35, Neither:52 'Group': ['Individual', 'Individual', 'Individual'] }) dfGen = pd.DataFrame({ 'Winner': ['Dog', 'Cat', 'Neither'], 'percent': [20, 53-20, 100-53], # Dog:20, Cat:33, Neither:47 'Group': ['General', 'General', 'General'] }) # 合并为统一数据集 df = pd.concat([dfInd, dfGen], ignore_index=True)
2. 正确绘制堆叠条形图
利用seaborn.barplot的bottom参数实现堆叠,同时指定y="Group"区分两个条形的Y轴位置,所有绘图统一使用同一个坐标轴:
sns.set_theme(style="whitegrid") f, ax = plt.subplots(figsize=(10, 6)) # 定义类别颜色映射 color_map = {'Dog': '#756bb1', 'Cat': '#bcbddc', 'Neither': '#efedf5'} # 按顺序绘制每个堆叠层 for winner in ['Dog', 'Cat', 'Neither']: # 计算当前层的底部位置(前序所有类别占比的累计和) prev_winners = ['Dog', 'Cat'][:list(color_map.keys()).index(winner)] bottom = df[df['Winner'].isin(prev_winners)].groupby('Group')['percent'].sum().reindex(['Individual', 'General'], fill_value=0) # 绘制当前层 sns.barplot( x='percent', y='Group', data=df[df['Winner'] == winner], color=color_map[winner], ax=ax, bottom=bottom.values ) # 美化图表 ax.set_xlabel('Percentage') ax.set_ylabel('') ax.set_title('100% Stacked Bar Chart') handles = [plt.Rectangle((0,0),1,1, color=color_map[w]) for w in color_map] ax.legend(handles, color_map.keys(), title='Winner') plt.tight_layout() plt.show()
3. 关键说明
- 相对百分比:必须将累计值转换为每个类别的单独占比,堆叠后才能准确达到100%。
- bottom参数:指定当前条形的底部位置为前序类别占比的总和,是实现堆叠效果的核心。
- 统一坐标轴:所有绘图指定同一个
ax,避免多轴重叠问题。
更简洁的替代方案(使用matplotlib原生绘制)
如果不需要seaborn的样式,直接用matplotlib的堆叠条形图功能会更高效:
import pandas as pd import matplotlib.pyplot as plt # 转换为宽格式数据,适配matplotlib堆叠需求 df_wide = pd.DataFrame({ 'Group': ['Individual', 'General'], 'Dog': [13, 20], 'Cat': [35, 33], 'Neither': [52, 47] }) colors = ['#756bb1', '#bcbddc', '#efedf5'] ax = df_wide.set_index('Group').plot(kind='barh', stacked=True, color=colors, figsize=(10,6)) ax.set_xlabel('Percentage') ax.set_title('100% Stacked Bar Chart') plt.legend(title='Winner') plt.tight_layout() plt.show()
内容的提问来源于stack exchange,提问作者oymonk
相关产品推荐
相关产品推荐

