该用pd.cut当需按固定区间或等宽分箱(如年龄分段),必须用pd.qcut当需等频分箱以应对偏态分布;qcut易因重复值报错,可设duplicates='drop'或预处理去重。

什么时候该用 pd.cut,什么时候必须用 pd.qcut
pd.cut 按数值区间切分,适合你明确知道边界(比如年龄分段:0–18、18–60、60+),或者想等宽分箱;pd.qcut 按分位数切分,保证每箱样本量大致相等,适合分布不均的数据(比如收入、点击时长这类右偏数据)。如果直接对偏态数据用 cut,可能前几箱空、后几箱挤满——这不是 bug,是设计如此。
常见错误现象:ValueError: Bin edges must be unique,多出现在 qcut 对含大量重复值(如 0 填充的点击次数)做等频分箱时。这时要么加 duplicates='drop',要么先用 rank(method='first') 打散并列值。
-
cut的bins可以是整数(等宽)、列表(自定义断点)、或pd.IntervalIndex -
qcut的q参数支持整数(如q=4表示四分位)、列表(如[0, 0.25, 0.5, 0.75, 1]) - 两者都默认返回
Categorical,若需数字标签,加labels=False;若需字符串区间描述,保持默认或传入labels列表
cut 的边界陷阱:左闭右开 vs 左开右闭
pd.cut 默认是左开右闭(include_lowest=False),即区间形如 (a, b],最小值容易被漏掉。比如 cut([1,2,3,4], bins=2) 会生成 (0.999, 2.5] 和 (2.5, 4.0],而 1 落在第一箱——但如果你手动写了 bins=[1,3,5],又没设 include_lowest=True,那值等于 1 的样本会被标为 NaN。
- 安全做法:显式指定
include_lowest=True,尤其当数据含已知极小值时 - 若需左闭右开(
[a, b)),目前 pandas 不原生支持,得靠pd.IntervalIndex.from_breaks(..., closed='left')配合cut(..., bins=interval_index) - 注意
right=False参数已被弃用,别再用
qcut 分箱后出现 NaN 的真实原因
不是数据有缺失,而是 qcut 在计算分位数时,对重复值密集的尾部(尤其是 0 或极小值扎堆)无法严格划分出指定数量非空箱。例如对 1000 个样本中 950 个是 0 的列执行 qcut(..., q=5),它尝试找 5 个分位点,但前 95% 都卡在 0,导致第 1–4 箱全空或边界重叠,最终部分值无法归类。
- 验证方法:先跑
df['col'].quantile([0, 0.2, 0.4, 0.6, 0.8, 1]),看输出是否有重复值 - 解决路径一:加
duplicates='drop'(丢弃重复分位点,箱数可能少于预期) - 解决路径二:改用
cut+ 自定义bins=np.percentile(..., [...]),手动去重后再传入 - 慎用
qcut(..., retbins=True)后直接拿bins去cut—— 因为qcut内部用了插值,retbins返回的未必能完美覆盖原始数据范围
性能与内存:大数据量下别无脑用 qcut
qcut 必须全局排序 + 计算分位数,时间复杂度 O(n log n),而 cut 是 O(n) 查表。百万级数据跑 qcut(..., q=10) 可能比 cut 慢 3–5 倍;更麻烦的是,qcut 默认返回 Categorical,但如果原始数据是 float64 且分箱数多(如 q=100),每个箱的区间字符串会反复构造,内存占用飙升。
- 提速技巧:对大数组先
values转numpy.ndarray,再调用pd.qcut,避免 DataFrame 索引开销 - 省内存技巧:确定不需要区间语义时,强制
labels=False,返回整数编码而非Categorical - 替代思路:用
sklearn.preprocessing.KBinsDiscretizer(strategy='quantile'),底层 C 实现,对超大数据更稳
真正难的不是语法,是判断「这列数据到底适不适合等频」——看直方图、算 skewness、检查重复值比例,比敲命令重要得多。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











