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

如何解决Seaborn带Hue的CountPlot中首个X轴刻度的错误标注问题

解决Seaborn CountPlot(带Hue)首个X轴刻度错误标注0的问题

问题场景

使用Seaborn进行双变量分析时,针对分类变量使用带hue参数的countplot绘制图表,会在首个X轴刻度处错误显示0的百分比标注,该问题仅出现在带hue的图表中;数值变量使用barplot展示,分类变量使用带hue的countplot展示,原代码如下:

categorical_var = ['Complains', 'Charge Amount', 'Age Group', 'Tariff Plan', 'Status']
numerical_var = ['Call Failures', 'Subscription Length', 'Seconds of Use', 'Frequency of use', 
                 'Frequency of SMS', 'Distinct Called Numbers','Customer Value']

def bi_var_analysis(category,target_variables):

    fig, axes = plt.subplots(2, 3, figsize=(18, 10))
    axes = axes.flatten()
    
    for i, col in enumerate(target_variables):
        if col in categorical_var:
            countplot = sns.countplot(x=df[category], ax=axes[i], hue=df[col])
            axes[i].set_title(f'{col} based on {category}')

            total = len(df[category])
            for p in countplot.patches:
                percentage = '{:.1f}%'.format(100 * p.get_height() / total)
                x = p.get_x() + p.get_width() / 2
                y = p.get_height()
                axes[i].annotate(percentage, (x, y), ha='center', va='bottom')

        else:
            barplot = sns.barplot(x=df[category], y=df[col], ax=axes[i])
            axes[i].set_title(f'{category} vs Average {col}')
            axes[i].set_xlabel(category)
            axes[i].set_ylabel(col)

            for p in barplot.patches:
                        height = p.get_height()
                        axes[i].annotate(f'{height:.2f}', (p.get_x() + p.get_width() / 2, height), 
                                         ha='center', va='bottom')

    plt.tight_layout()
    plt.show()

问题原因

  1. 当使用hue参数时,countplot会为每个X类别生成对应hue分组的条形,若某个hue分组在某个X类别下没有数据,对应的条形高度为0,原代码未过滤这种情况,导致生成0%的标注。
  2. 原代码中计算百分比使用的是整个数据集的总数,而非当前X类别下的样本总数,这会导致百分比计算不符合实际分组占比的预期。

修复方案

  1. 遍历countplot.patches时,跳过高度为0的条形,避免生成0%的标注。
  2. 按X类别分组计算样本总数,确保百分比是当前X类别下各hue分组的占比(更符合双变量分析的逻辑)。

修复后的完整代码:

import seaborn as sns
import matplotlib.pyplot as plt

categorical_var = ['Complains', 'Charge Amount', 'Age Group', 'Tariff Plan', 'Status']
numerical_var = ['Call Failures', 'Subscription Length', 'Seconds of Use', 'Frequency of use', 
                 'Frequency of SMS', 'Distinct Called Numbers','Customer Value']

def bi_var_analysis(category, target_variables):
    fig, axes = plt.subplots(2, 3, figsize=(18, 10))
    axes = axes.flatten()
    
    # 预计算每个X类别的样本总数
    category_counts = df[category].value_counts()
    
    for i, col in enumerate(target_variables):
        if col in categorical_var:
            countplot = sns.countplot(x=df[category], ax=axes[i], hue=df[col])
            axes[i].set_title(f'{col} based on {category}')

            for p in countplot.patches:
                height = p.get_height()
                # 跳过高度为0的条形,不生成标注
                if height == 0:
                    continue
                
                # 获取当前条形对应的X类别标签
                x_idx = int(p.get_x() // p.get_width())
                x_label = axes[i].get_xticklabels()[x_idx].get_text()
                # 使用当前X类别的总数计算百分比
                category_total = category_counts[x_label]
                percentage = '{:.1f}%'.format(100 * height / category_total)
                
                x = p.get_x() + p.get_width() / 2
                y = height
                axes[i].annotate(percentage, (x, y), ha='center', va='bottom')

        else:
            barplot = sns.barplot(x=df[category], y=df[col], ax=axes[i])
            axes[i].set_title(f'{category} vs Average {col}')
            axes[i].set_xlabel(category)
            axes[i].set_ylabel(col)

            for p in barplot.patches:
                height = p.get_height()
                axes[i].annotate(f'{height:.2f}', (p.get_x() + p.get_width() / 2, height), 
                                 ha='center', va='bottom')

    plt.tight_layout()
    plt.show()

关键修改点

  • 添加if height == 0: continue过滤无数据的条形,消除0标注。
  • 用category_counts = df[category].value_counts()预计算每个X类别的样本数,替换原有的全局总数,使百分比计算更合理。
  • 通过x_idx = int(p.get_x() // p.get_width())获取当前条形对应的X类别索引,进而得到该类别的总数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 12:17:38