自学教程 学编程从这里开始

Pandas 数据分箱 cut /qcut

承接《Pandas 透视表 pivot_table》,本篇讲解 pd.cut 和 pd.qcut 分箱函数,用于连续数值离散化,对比两者原理、边界设置、标签、异常处理、业务场景,是特征工程、用户分层、报表区间统计高频工具,保持系列教程统一风格。
前置导入与示例数据

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "name": ["张三","李四","王五","赵六","孙七","周八","吴九","郑十"],
    "age": [18, 22, 25, 30, 35, 42, 50, 61],
    "income": [4500, 7200, 9800, 12000, 15600, 18000, 24000, 32000]
})
print(df)

一、cut 与 qcut 一句话区别

  • pd.cut:按自定义区间边界分箱(等距分箱),区间宽度固定,每个箱子样本数量不一定均匀
  • pd.qcut:按分位数分箱(等频分箱),每个箱子里面样本数量尽量一样,区间宽度不固定

适用场景:连续数字 → 转换成分类标签(年龄段、收入档位、风险等级)

二、pd.cut 基础用法

2.1 等距分箱,直接指定箱子数量

# age 分成3个区间
df["age_bin_cut"] = pd.cut(df["age"], bins=3)
print(df[["name","age","age_bin_cut"]])

输出会自动生成区间,默认左开右闭 (a, b]。

2.2 手动指定边界(业务最常用)

bins = [0, 25, 35, 45, 100]
labels = ["青年","壮年","中年","老年"]
df["age_level"] = pd.cut(
    df["age"],
    bins=bins,
    labels=labels,
    right=True  # right=True:左开右闭;right=False:左闭右开
)
print(df[["name","age","age_level"]])
  • right=True(默认):(0,25],包含25
  • right=False:[0,25),不包含25

2.3 include_lowest 包含最小值

边界最小值默认是开区间,如果原始数据刚好等于最小值,会归为NaN,开启 include_lowest=True 解决:

df["age_level"] = pd.cut(
    df["age"],
    bins=[18,30,45,100],
    labels=["青年","中年","老年"],
    include_lowest=True
)

三、pd.qcut 分位数分箱(等频)

qcut 依靠分位数,保证每个分组里面样本数量基本一致。

# 分成4组,每组人数尽量相等
df["income_qbin"] = pd.qcut(df["income"], q=4)
# 自定义标签
df["income_level"] = pd.qcut(
    df["income"],
    q=4,
    labels=["低收入","中低收入","中高收入","高收入"]
)
print(df[["name","income","income_level"]])

q 可以传入分位数列表,自定义分割位置:

# 20%、50%、80% 分位点
df["income_q2"] = pd.qcut(df["income"], q=[0,0.2,0.5,0.8,1], labels=["A","B","C","D"])

⚠️ qcut 坑:如果大量重复值,无法均分,会报错,设置 duplicates="drop" 跳过重复分位数

pd.qcut(df["income"], q=4, duplicates="drop")

四、返回值:retbins=True 获取分割边界

有时候需要拿到分割阈值,用于后续新数据预测:

s_bin, edges = pd.cut(df["age"], bins=3, retbins=True)
print(edges)

qcut 同样支持 retbins。

五、参数汇总

pd.cut 参数

  • x:一维Series/数组
  • bins:int(箱子数)或者列表(自定义边界)
  • labels:分组标签,None则返回区间
  • right:是否右闭区间
  • include_lowest:是否把最小值纳入第一个区间
  • retbins:是否返回边界
  • precision:小数精度

pd.qcut 参数

  • q:int(分组数量)或者分位数列表 [0,0.25,0.5,0.75,1]
  • duplicates:drop 用来处理大量重复值导致分位数重合报错

六、cut 和 qcut 对比

函数分割依据箱子宽度样本数量适合场景
cut固定区间边界相等不一定均匀年龄、金额区间、固定档位报表
qcut分位数不相等尽量均匀分群、排名分层、特征分箱

七、搭配 value_counts 统计各箱数量

# 统计cut分箱数量
df["age_level"].value_counts().sort_index()
# qcut分箱统计
df["income_level"].value_counts().sort_index()

八、分组 + 分箱(业务实战)

# 每个地区内部,收入做等频分层
df2 = pd.DataFrame({
    "area":["A","A","A","A","B","B","B","B"],
    "income":[3000,5000,8000,12000,4000,6500,9500,15000]
})
df2["income_tier"] = df2.groupby("area")["income"].transform(lambda x: pd.qcut(x, q=2, labels=["低","高"]))
print(df2)

九、高频坑清单

  1. cut 手动bins时,数据超出区间范围 → 返回NaN,需要把边界拉大覆盖全部数据
  2. right=True 容易搞混边界,比如 25 到底属于上一组还是下一组
  3. qcut 遇到大量相同值,分位数重复,直接抛出 ValueError,加 duplicates="drop"
  4. 标签labels长度必须和箱子数量一致,数量不对直接报错
  5. 分箱后得到的是Categorical类型,排序会按照区间顺序,不是字符串字典序
  6. 分箱结果可以直接用于 groupby 分组统计

十、综合实战模板

def bin_by_custom(s: pd.Series, bins, labels):
    """自定义区间分箱"""
    return pd.cut(
        s,
        bins=bins,
        labels=labels,
        right=True,
        include_lowest=True
    )

def bin_by_quantile(s: pd.Series, n_group):
    """等频分箱"""
    return pd.qcut(
        s,
        q=n_group,
        duplicates="drop"
    )

df["age_tier"] = bin_by_custom(df["age"], bins=[0,25,35,45,100], labels=["青年","壮年","中年","老年"])
df["income_tier"] = bin_by_quantile(df["income"], 4)
print(df[["name","age","age_tier","income","income_tier"]])

十一、速查表

需求选择
固定档位,如 0-25,25-35cut + 列表bins
平均分成N等宽区间cut + bins=N
每组人数尽量一样qcut
存在大量重复值做等频分箱qcut + duplicates=”drop”
标签:

0 条笔记