一句话总结: pd.cut 将连续数值切分为固定区间,配合标签和虚拟编码,实现数值特征到类别特征的转换。
📋 环境要求
- Python 3.14+
- scikit-learn 1.9+
- pandas 3.0+
- numpy 2.4+
- jupyter 1.1+
- notebook 7.5+
- nbconvert 7.17+
一、为什么需要数值离散化?
连续型数值在某些场景下不如离散区间直观:
- 业务解释:年龄段比具体年龄更易理解(青年/中年/老年)
- 降低噪声:平滑异常值对模型的影响
- 适配算法:决策树、朴素贝叶斯等算法对类别型更友好
- 规则表达:"体重超过 80kg 为超重"比回归系数更直观
核心工具: pandas 的 cut() 函数,基于区间边界做等宽划分。
二、等宽区间划分
场景: 将数据自动划分为 N 个等宽区间。
import pandas as pd
df = pd.DataFrame(data={'weight': [75., 78.5, 85., 91., 84.5, 83., 68.]})
# bins=3: 自动计算区间边界,将数据划分为 3 个等宽区间
# 区间宽度 = (最大值 91 - 最小值 68) / 3 ≈ 7.67
df["weight_cut"] = pd.cut(df["weight"], bins=3)
划分结果:
| weight | weight_cut |
|---|---|
| 75.0 | (67.977, 75.667] |
| 78.5 | (75.667, 83.333] |
| 85.0 | (83.333, 91.0] |
| 91.0 | (83.333, 91.0] |
| 84.5 | (83.333, 91.0] |
| 83.0 | (75.667, 83.333] |
| 68.0 | (67.977, 75.667] |
关键点:
- 区间左开右闭:
(67.977, 75.667]表示大于 67.977 且小于等于 75.667 - 自动生成边界:覆盖数据范围并适当外延
- 返回
category类型,节省内存
三、指定区间边界
场景: 业务有明确区间定义(如 BMI 分级),需自定义边界。
df = pd.DataFrame(data={'weight': [75., 78.5, 85., 91., 84.5, 83., 68.]})
# bins=[60, 75, 80, 95]: 自定义三个区间 (60,75], (75,80], (80,95]
# 注意:边界值 75 归入 (60,75],边界值 80 归入 (75,80]
df["weight_cut"] = pd.cut(df["weight"], bins=[60, 75, 80, 95])
与自动划分的区别:
| 方式 | 区间 | 适用场景 |
|---|---|---|
| 自动划分 | (67.977, 75.667] | 探索性分析,无先验知识 |
| 指定边界 | (60, 75] | 业务规则明确,如体重分级 |
关键点:
- 自定义边界更贴合业务语义
- 数据超出边界范围会产生 NaN
- 边界值归属遵循右闭原则
四、添加区间标签
场景: 将区间转换为易读的标签(如轻/中/重)。
df = pd.DataFrame(data={'weight': [75., 78.5, 85., 91., 84.5, 83., 68.]})
# labels: 为每个区间指定可读标签
# bins 数量 = labels 数量 + 1
df["weight_cut"] = pd.cut(
df["weight"],
bins=[60, 75, 80, 95],
labels=["light", "normal", "heavy"] # 对应三个区间
)
结果:
| weight | weight_cut |
|---|---|
| 75.0 | light |
| 78.5 | normal |
| 85.0 | heavy |
| 91.0 | heavy |
| 84.5 | heavy |
| 83.0 | heavy |
| 68.0 | light |
关键点:
labels长度必须等于bins数量减 1- 标签顺序与区间顺序一一对应
- 标签使数据更具业务可读性
五、虚拟编码(One-Hot 编码)
场景: 将类别型特征转换为数值型,供机器学习模型使用。
df = pd.DataFrame(data={'weight': [75., 78.5, 85., 91., 84.5, 83., 68.]})
df["weight_cut"] = pd.cut(
df["weight"],
bins=(60, 75, 80, 95),
labels=['light', 'normal', 'heavy']
)
# pd.get_dummies: 将类别列展开为多列布尔值
df = pd.get_dummies(df)
编码结果:
| weight | weight_cut_light | weight_cut_normal | weight_cut_heavy |
|---|---|---|---|
| 75.0 | True | False | False |
| 78.5 | False | True | False |
| 85.0 | False | False | True |
| 91.0 | False | False | True |
| 84.5 | False | False | True |
| 83.0 | False | False | True |
| 68.0 | True | False | False |
关键点:
- 每行只有一个
True,其余为False - 列名格式:
原列名_类别标签 - 适用于线性模型、神经网络等需要数值输入的算法
- 决策树类算法可直接使用 category 类型,无需虚拟编码
六、常见问题
Q1: 区间边界值归属哪一侧?
pd.cut 默认右闭左开:(a, b] 表示 a < x <= b。
# 75.0 归入 (60, 75],而非 (75, 80]
pd.cut([75.0], bins=[60, 75, 80]) # [(60, 75.0]]
如需左闭右开,使用 right=False:
pd.cut([75.0], bins=[60, 75, 80], right=False) # [[60, 75)]
Q2: 数据超出边界怎么办?
# 55 和 100 超出 [60, 95] 范围
df["weight_cut"] = pd.cut([55, 70, 100], bins=[60, 75, 95])
# 结果: [NaN, (60, 75], NaN]
解决: 扩大边界或添加 include_lowest=True:
pd.cut([55, 70], bins=[50, 75, 100]) # 扩大边界
Q3: 等宽 vs 等频划分?
| 方法 | 函数 | 特点 | 适用场景 |
|---|---|---|---|
| 等宽 | pd.cut | 区间宽度相等 | 数据均匀分布 |
| 等频 | pd.qcut | 每区间样本数相等 | 数据偏态分布 |
# 等频划分:每个区间约 1/3 的数据
pd.qcut(df["weight"], q=3, labels=["low", "mid", "high"])
七、速查表
pd.cut 参数
| 参数 | 说明 | 示例 |
|---|---|---|
x | 待划分的数组/Series | df["weight"] |
bins | 区间数量或边界列表 | 3 或 [60, 75, 95] |
labels | 区间标签 | ["light", "heavy"] |
right | 是否右闭区间 | True(默认) / False |
include_lowest | 是否包含最小值 | False(默认) |
完整流程模板
# 1. 离散化
df["category"] = pd.cut(df["value"], bins=[0, 50, 100, 150], labels=["低", "中", "高"])
# 2. 虚拟编码(模型需要时)
df_encoded = pd.get_dummies(df, columns=["category"])
八、总结
数值离散化是数据预处理中的重要步骤,pd.cut 提供了灵活且强大的区间划分能力:
- 等宽划分:适用于探索性分析,无先验知识时使用
- 指定边界:适用于业务规则明确的场景
- 添加标签:提升数据的可读性和业务解释性
- 虚拟编码:为机器学习模型准备数值输入
通过合理使用离散化技术,可以将连续数值转换为更具业务意义的类别特征,提升模型的可解释性和稳定性。
网络取材来源于: ant-exercises-sklearn: scikit-learn 编程练习 100例
刚开始学习数据集的处理,在这里同步学习成果、激励自己坚持到底。
7459

被折叠的 条评论
为什么被折叠?



