
本文介绍使用pandas对非序数型分类列进行智能分箱:选取累计占比达50%以上的高频类别保留,其余统一标记为"others",避免手动循环和链式赋值错误。
本文介绍使用pandas对非序数型分类列进行智能分箱:选取累计占比达50%以上的高频类别保留,其余统一标记为"others",避免手动循环和链式赋值错误。
在实际数据预处理中,常需对高基数(high-cardinality)分类变量做降维处理——例如将出现频率累计达50%以上的头部类别保留,其余低频值统一归入 "others" 类别。这既能保留主要分布特征,又能显著减少独热编码(One-Hot Encoding)后的维度爆炸问题。
但直接使用 for 循环配合索引赋值(如 df['column'][i] = 'others')存在两个关键问题:
1️⃣ 链式赋值警告与失效:df['column'][i] 是视图(view)而非副本,修改可能不生效或触发 SettingWithCopyWarning;
2️⃣ 逻辑错位:原代码中 i 是 value_counts() 返回 Series 的位置索引,而 df['column'][i] 却试图用该索引去访问原始 DataFrame 行号——二者索引不一致,导致赋值完全错位。
✅ 正确做法是:基于值(value)而非位置(position)进行布尔筛选与批量赋值。推荐使用以下三步法:
- 计算归一化频次并排序(默认降序);
- 用 .cumsum() 累计求和,再用 .gt(0.5) 找出首次超过50%的分界点之后的所有类别;
- 利用 .isin() 定位原始 DataFrame 中属于这些“低频类”的所有行,并统一赋值。
# 正确实现:累计占比 ≥50% 的头部类别保留,其余设为 'others' counts_norm = df['column'].value_counts(normalize=True).round(2) cumulative_mask = counts_norm.cumsum().gt(0.5) # True 表示该类别应被归为 others low_freq_categories = counts_norm.index[cumulative_mask].tolist() # 批量安全赋值(推荐 loc) df.loc[df['column'].isin(low_freq_categories), 'column'] = 'others'
? 关键技巧说明:
- value_counts(normalize=True) 默认按频次降序排列,确保 cumsum() 从最高频开始累加;
- cumsum().gt(0.5) 返回布尔 Series,其 True 对应的是首个使累计和突破0.5的类别及其之后的所有类别;
- df.loc[condition, col] = value 是 Pandas 推荐的显式赋值方式,规避链式赋值风险。
? 注意事项:
- 若多个类别频次相同且恰好卡在累计阈值边界(如0.49→0.51),round(2) 可能影响判断精度,生产环境建议保留更高精度(如 round(4))或改用 cumsum() >= 0.5;
- 对于空值(NaN),value_counts() 默认忽略,若需保留 NaN 作为一类,请先用 dropna=False 参数;
- 若需保留至少一个类别(防止全变为 'others'),可在 cumsum().gt(0.5) 后加 .iloc[1:] = False 或使用 cumsum().shift(1, fill_value=0).lt(0.5) 更稳健地定义“头部”。
执行后,可通过 df['column'].value_counts(dropna=False) 验证结果:高频类别原样保留,其余统一归入 'others',逻辑清晰、性能优异,适用于任意规模数据集。

















