实现基于label列、带计数/百分比标注的任意列堆叠柱状图
堆叠柱状图(带计数与百分比)实现方案
原始数据
import pandas as pd import matplotlib.pyplot as plt import matplotlib print('matplotlib: {}'.format(matplotlib.__version__)) # 输出:3.5.3 df=pd.DataFrame({'Type': [ 'Sentence', 'Array', 'String', '-','-', 'Sentence', 'Array', 'String', '-','-', 'Sentence'], 'Length': [42,21,11,6,6,42,21,11,6,6,42], 'label': [1,1,0,0,0,1,1,0,0,0,1], }) print(df) # 输出结果: # Type Length label # 0 Sentence 42 1 # 1 Array 21 1 # 2 String 11 0 # 3 - 6 0 # 4 - 6 0 # 5 Sentence 42 1 # 6 Array 21 1 # 7 String 11 0 # 8 - 6 0 # 9 - 6 0 # 10 Sentence 42 1
需求说明
针对DataFrame中的任意列(数值列如Length或分类列如Type),以label列为堆叠依据绘制堆叠柱状图,同时标注计数和百分比,且稀有观测的小数值也需正常显示。
现有问题分析
直接使用df.plot.bar(stacked=True)无法正确统计分组计数;使用groupby+pivot_table时出现TypeError: unsupported operand type(s) for +: 'int' and 'str'错误,核心原因是未正确统计目标列与label的分组频次,且百分比计算逻辑有误。
解决方案一:Matplotlib + Seaborn 实现(通用函数)
编写通用函数,传入目标列名即可生成符合要求的图表:
import seaborn as sns import matplotlib.pyplot as plt def plot_stacked_bar(df, target_col): # 1. 统计目标列与label的分组计数 count_df = df.groupby([target_col, 'label']).size().unstack(fill_value=0) # 2. 计算每行的占比百分比 percent_df = count_df.div(count_df.sum(axis=1), axis=0) * 100 # 3. 绘制堆叠柱状图 ax = count_df.plot(kind='bar', stacked=True, figsize=(10,6), rot=0) # 4. 添加计数+百分比标注 for container in ax.containers: counts = [v.get_height() for v in container] percents = [percent_df.iloc[i, int(container.get_label())] for i, v in enumerate(container)] ax.bar_label(container, labels=[f'{cnt}\n({pct:.1f}%)' if cnt > 0 else '' for cnt, pct in zip(counts, percents)], label_type='center', fontsize=9) # 5. 调整图例与标题 ax.legend(title='标签', labels=['0: 正常', '1: 异常'], bbox_to_anchor=(1, 1.02), loc='upper left') ax.set_title(f'按{target_col}分组的堆叠柱状图(带计数与百分比)', fontsize=12) ax.set_xlabel(target_col, fontsize=10) ax.set_ylabel('计数', fontsize=10) plt.tight_layout() plt.show()
调用示例
针对分类列Type
plot_stacked_bar(df, 'Type')
生成的图表与预期输出一致,每个堆叠块清晰标注计数和对应百分比(如Sentence的label=1计数为3,占比100%)。
针对数值列Length
plot_stacked_bar(df, 'Length')
按Length的不同取值分组,展示每个长度对应的label分布情况。
解决方案二:Plotly 实现(交互式图表)
如果需要交互式体验,Plotly可生成支持hover查看详情的堆叠柱状图:
import plotly.express as px import plotly.graph_objects as go def plot_stacked_bar_plotly(df, target_col): # 统计分组计数与总数 count_df = df.groupby([target_col, 'label']).size().reset_index(name='count') total_counts = df.groupby(target_col).size().reset_index(name='total') count_df = count_df.merge(total_counts, on=target_col) count_df['percent'] = (count_df['count'] / count_df['total']) * 100 # 创建堆叠柱状图 fig = px.bar(count_df, x=target_col, y='count', color='label', color_discrete_map={0: '#1f77b4', 1: '#ff7f0e'}, labels={'label': '标签', 'count': '计数', target_col: target_col}, title=f'按{target_col}分组的堆叠柱状图') # 添加文本标注 for idx, row in count_df.iterrows(): # 计算标注的y轴位置(堆叠块的中心) prev_sum = count_df[(count_df[target_col]==row[target_col]) & (count_df['label'] < row['label'])]['count'].sum() fig.add_annotation( x=row[target_col], y=prev_sum + row['count']/2, text=f'{row["count"]}\n({row["percent"]:.1f}%)', showarrow=False, font_size=10 ) # 调整图例布局 fig.update_layout( legend=dict(title='标签', items=[ dict(label='0: 正常', color='#1f77b4'), dict(label='1: 异常', color='#ff7f0e') ], bbox_to_anchor=(1, 1), yanchor='top'), title_x=0.5 ) fig.show()
调用示例
plot_stacked_bar_plotly(df, 'Type')
生成的交互式图表支持hover查看详细数据,标注清晰直观。
内容的提问来源于stack exchange,提问作者Mario
相关产品推荐
相关产品推荐

