查看全课大纲(第 14 / 35 节)
- 1.特征工程简介
- 2.特征工程的处理流程
- 3.数据与特征概述
- 4.数据统计、相似度与数据探索
- 5.数据探索工具 Pandas_profiling
- 6.工具应用:《泰坦尼克》分析报告
- 7.构造一个微型链家租房数据集用于演示
- 8.数据预处理综述
- 9.缺失值处理实操
- 10.异常值处理实操
- 11.连续值特征构建
- 12.类别型数据特征构建
- 13.特征幅度缩放
- 14.特征离散化
- 15.特征编码:让类别数据参与建模
- 16.特征变换
- 17.时间特征
- 18.特征衍生
- 19.自动化特征工具
- 20.特征选择综述
- 21.特征选择方法
- 22.过滤式特征选择方法实操
- 23.包裹式特征选择方法
- 24.嵌入式特征选择
- 25.混序法与混合法特征选择
- 26.特征降维综述
- 27.案例实操:特征降维
- 28.特征监控概述与指标
- 29.Iris 数据集特征漂移检测案例
- 30.特征监控实战:用分布对比检测数据漂移
- 31.实时特征监控
- 32.电商销量预测项目背景介绍
- 33.数据预处理和特征工程(一)
- 34.数据预处理和特征工程(二)
- 35.数据建模
特征离散化
约 9 分钟
小象实战讲义 · 特征工程精讲与实践
在特征工程中,我们常常会遇到连续值特征,如年龄、收入、商品价格等。直接使用这些连续值进行建模,有时并不能很好地捕捉数据中的非线性关系,或者模型容易受到极端值的干扰。本节我们将学习特征离散化(也称为分箱或分桶),这是一种将连续值特征转换为离散区间的技术,能够增强线性模型的非线性表达能力,并提升模型的健壮性。
💡 核心导读
- 离散化的作用:将连续特征分段,赋予线性模型非线性能力,并增强模型抗干扰性。
- 无监督离散化方法:掌握等距切分、等频切分和K-Means切分的原理与实现。
- 有监督离散化方法:学习如何利用决策树和卡方检验(ChiMerge)进行更贴合标签分布的离散化。
- 实践要点:理解离散化器的“先拟合后变换”流程,并学会将处理方式应用到新数据上。
离散化的作用与原理
特征离散化,顾名思义,是对连续值属性进行处理的一种方法。其核心思想是把连续的数值范围切分成若干个段(或称为桶、箱),然后将每个原始值归属到对应的段中。
这样做主要有两大好处:
- 引入非线性:对于逻辑回归等线性模型,离散化可以将一个连续特征转换为多个“开关”特征(例如,通过独热编码),使得模型能够分段拟合数据,从而具备一定的非线性表达能力。
- 增强健壮性:离散化之后,数据被归入不同的桶中。即使原始数据存在极端值或噪声,只要它们落入同一个桶,就会被赋予相同的离散值,这在一定程度上平滑了数据,提高了模型的抗干扰能力。
在工业实践中,离散化也常被称为分箱(Binning)或分桶。
无监督离散化方法
无监督离散化不依赖目标标签,仅根据特征自身的分布进行切分。Scikit-learn提供了KBinsDiscretizer工具来实现多种无监督离散化策略。
等距切分(Uniform)
等距切分按照相同的步长将数据范围均匀地切成n个桶。每个桶的宽度(区间长度)是相等的。
import pandas as pd
import numpy as np
from sklearn.preprocessing import KBinsDiscretizer
# 构造一个简单的泰坦尼克票价数据样例
data = {'Fare': [0.0, 7.5, 8.7, 26.0, 512.0, 5.09, 1.8, 60.34, 27.57, 36.94]}
df = pd.DataFrame(data)
print("原始数据:")
print(df)
# 使用KBinsDiscretizer进行等距切分(3个桶)
enc_equal_width = KBinsDiscretizer(n_bins=3, encode='ordinal', strategy='uniform')
enc_equal_width.fit(df[['Fare']])
print("\n等距切分的桶边界:", enc_equal_width.bin_edges_)
# 对数据进行变换
result_equal_width = enc_equal_width.transform(df[['Fare']])
df['Fare_equal_width'] = result_equal_width.astype(int)
print("\n等距离散化后的数据:")
print(df[['Fare', 'Fare_equal_width']])原始数据:
Fare
0 0.00
1 7.50
2 8.70
3 26.00
4 512.00
5 5.09
6 1.80
7 60.34
8 27.57
9 36.94
等距切分的桶边界: [array([ 0. , 170.66666667, 341.33333333, 512. ])]
等距离散化后的数据:
Fare Fare_equal_width
0 0.00 0
1 7.50 0
2 8.70 0
3 26.00 0
4 512.00 2
5 5.09 0
6 1.80 0
7 60.34 0
8 27.57 0
9 36.94 0可以看到,区间被均匀分为[0, 170.67), [170.67, 341.33), [341.33, 512]三段。大部分样本都落在了第一个桶(编号0)中,因为票价分布极度右偏。等距切分简单直接,但完全不看数据的实际分布。
等频切分(Quantile)
等频切分旨在让每个桶内包含大致相同数量的样本。它通过数据的分位数来确定切分点。
import pandas as pd
import numpy as np
from sklearn.preprocessing import KBinsDiscretizer
# 构造一个简单的泰坦尼克票价数据样例
data = {'Fare': [0.0, 7.5, 8.7, 26.0, 512.0, 5.09, 1.8, 60.34, 27.57, 36.94]}
df = pd.DataFrame(data)
# 使用KBinsDiscretizer进行等频切分(3个桶)
enc_equal_freq = KBinsDiscretizer(n_bins=3, encode='ordinal', strategy='quantile')
enc_equal_freq.fit(df[['Fare']])
print("等频切分的桶边界:", enc_equal_freq.bin_edges_)
# 对数据进行变换
result_equal_freq = enc_equal_freq.transform(df[['Fare']])
df['Fare_equal_freq'] = result_equal_freq.astype(int)
print("\n等频离散化后的数据:")
print(df[['Fare', 'Fare_equal_freq']])等频切分的桶边界: [array([ 0. , 7.5 , 27.57, 512. ])]
等频离散化后的数据:
Fare Fare_equal_freq
0 0.00 0
1 7.50 1
2 8.70 1
3 26.00 1
4 512.00 2
5 5.09 0
6 1.80 0
7 60.34 2
8 27.57 2
9 36.94 2桶边界为[0, 7.5), [7.5, 27.57), [27.57, 512]。每个桶大约包含3-4个样本,数量基本一致。注意,由于可能存在重复的取值,等频切分不一定能做到每个桶绝对平均。
K-Means切分
K-Means切分利用K-Means聚类算法对特征值进行聚类——聚类中心本身并不是桶的边界,相邻两个聚类中心的 midpoint(中点)才是分桶边界。它试图使桶内的样本距离中心尽可能近,桶间尽可能远。
import pandas as pd
import numpy as np
from sklearn.preprocessing import KBinsDiscretizer
# 构造一个简单的泰坦尼克票价数据样例
data = {'Fare': [0.0, 7.5, 8.7, 26.0, 512.0, 5.09, 1.8, 60.34, 27.57, 36.94]}
df = pd.DataFrame(data)
# 使用KBinsDiscretizer进行K-Means切分(3个桶)
enc_kmeans = KBinsDiscretizer(n_bins=3, encode='ordinal', strategy='kmeans')
enc_kmeans.fit(df[['Fare']])
print("K-Means切分的桶边界:", enc_kmeans.bin_edges_)
# 对数据进行变换
result_kmeans = enc_kmeans.transform(df[['Fare']])
df['Fare_kmeans'] = result_kmeans.astype(int)
print("\nK-Means离散化后的数据:")
print(df[['Fare', 'Fare_kmeans']])K-Means切分的桶边界: [array([ 0. , 37.27, 286.17, 512. ])]
K-Means离散化后的数据:
Fare Fare_kmeans
0 0.00 0
1 7.50 0
2 8.70 0
3 26.00 0
4 512.00 2
5 5.09 0
6 1.80 0
7 60.34 1
8 27.57 0
9 36.94 0K-Means根据数据分布找到了三个聚类中心,并以此划分边界。它结合了数据分布和距离度量,是无监督方法中较为灵活的一种。
有监督离散化方法
有监督离散化利用目标标签的信息来寻找最优的切分点,使得离散化后的特征与标签的关系更强。
基于决策树的离散化
决策树天生就是通过选择特征的最佳切分点来进行分类的。我们可以训练一棵深度较浅的决策树(如深度为2或3),用连续特征预测目标标签。然后,将样本输入训练好的树,用其落入的叶节点的预测概率(或类别)作为离散化后的新特征值。
import pandas as pd
import numpy as np
from sklearn.tree import DecisionTreeClassifier
# 构造一个包含标签的小数据集
data = {
'Fare': [0.0, 7.5, 8.7, 26.0, 512.0, 5.09, 1.8, 60.34, 27.57, 36.94],
'Survived': [1, 0, 0, 0, 1, 1, 1, 0, 0, 0]
}
df_supervised = pd.DataFrame(data)
print("监督学习数据集:")
print(df_supervised)
# 使用决策树进行离散化(最大深度为2)
tree_model = DecisionTreeClassifier(max_depth=2, random_state=42)
tree_model.fit(df_supervised[['Fare']], df_supervised['Survived'])
# 使用predict_proba获取属于正类(Survived=1)的概率作为新特征
df_supervised['Fare_tree_discret'] = tree_model.predict_proba(df_supervised[['Fare']])[:, 1]
print("\n基于决策树离散化后的数据(前10行):")
print(df_supervised.head(10))
print("\n决策树离散化生成的特征唯一值:")
print(df_supervised['Fare_tree_discret'].unique())
# 查看根据决策树划分形成的近似分桶边界
bins = pd.concat([df_supervised.groupby(['Fare_tree_discret'])['Fare'].min(),
df_supervised.groupby(['Fare_tree_discret'])['Fare'].max()], axis=1)
bins.columns = ['min', 'max']
print("\n根据离散化结果推断的近似分桶边界:")
print(bins)监督学习数据集:
Fare Survived
0 0.00 1
1 7.50 0
2 8.70 0
3 26.00 0
4 512.00 1
5 5.09 1
6 1.80 1
7 60.34 0
8 27.57 0
9 36.94 0
基于决策树离散化后的数据(前10行):
Fare Survived Fare_tree_discret
0 0.00 1 1.0
1 7.50 0 0.0
2 8.70 0 0.0
3 26.00 0 0.0
4 512.00 1 1.0
5 5.09 1 1.0
6 1.80 1 1.0
7 60.34 0 0.0
8 27.57 0 0.0
9 36.94 0 0.0
决策树离散化生成的特征唯一值:
[1. 0.]
根据离散化结果推断的近似分桶边界:
min max
Fare_tree_discret
0.0 7.5 60.34
1.0 0.0 512.00观察结果:决策树学到的并不是一个单调关系,而是一个「两端高、中间低」的分段模式——低票价(≤约7.5)与极高票价(512)的样本被归入生存概率为 1 的叶节点,而中等票价区间(7.5~60.34)的样本被归入生存概率为 0 的叶节点。这正是有监督离散化的价值所在:它自动找到了贴合标签分布的切分点;而等距、等频这类无监督方法对「标签与特征之间的非线性关系」是完全无感的。这也解释了为什么金融风控等场景更偏好有监督的分桶。
卡方合并(ChiMerge)
卡方合并是一种自底向上的监督离散化方法。它最初将每个唯一值视为一个区间,然后计算相邻区间的卡方统计量。卡方值衡量了两个区间内类别分布的相似性。如果卡方值小于某个阈值(如对应p=0.05,自由度为1的卡方值3.841),则认为两个区间分布相似,可以合并。该过程迭代进行,直到所有相邻区间的卡方值都超过阈值,或达到预设的桶数量。
这种方法在金融风控等领域应用广泛,因为它能自动合并分布相似的区间,找到有区分度的分桶。
# 卡方合并的实现相对复杂,通常需要自定义或使用第三方库。
# 其核心思想是:基于相邻区间类别分布的卡方检验,不断合并最相似的区间。
# 原课程中使用了封装好的 ChiMerge 工具类。
# 以下为使用逻辑的示意性描述(伪代码,不实际运行):
# 1. 初始化:将连续特征每个排序后的唯一值作为一个初始区间。
# 2. 计算相邻区间的卡方统计量。
# 3. 找到卡方值最小的一对相邻区间。
# 4. 如果最小卡方值小于阈值,则合并这对区间,并回到步骤2。
# 5. 否则,停止合并,得到最终的分桶方案。离散化实践要点总结
- 先拟合后变换:所有离散化器(无论是
KBinsDiscretizer还是自定义的监督离散化器)都需要先在训练集上fit,学习切分边界,然后在训练集和测试集上分别transform。绝对不能用测试集单独fit,这会导致数据泄露。 - 方法选择:
- 无监督方法(等距、等频、K-Means):计算快,不依赖标签,适用于探索性分析或标签不可用时。
- 有监督方法(决策树、ChiMerge):利用标签信息,离散化后的特征通常与目标关联更强,可能提升模型效果,但计算成本稍高,且需要防止过拟合。
- 参数调优:桶的数量(
n_bins)、决策树的深度等是重要参数。可以通过交叉验证来评估不同离散化方式对最终模型性能的影响。 - 特征衍生:离散化本身产生的是离散的桶编号(如0,1,2)。我们通常需要进一步对这些编号进行独热编码或目标编码,才能将其输入到线性模型中,从而真正实现非线性表达。
📝 动手练一练
- 概念辨析:等距离散化与等频离散化在数据分布极度不平衡(如存在大量0值和少量极大值)时,产生的桶内样本分布会有何不同?哪种方法可能更适合后续建模?
👉 点击查看参考答案
等距离散化会按照数值范围均匀分桶。在数据极度不平衡时,可能导致绝大多数样本集中在第一个桶,而后面几个桶样本极少甚至为空,这会使离散化特征的信息量很低。等频离散化则保证每个桶的样本量大致相同,能更好地利用数据分布,将稀少的大值样本也单独成桶或合并到有样本的桶中,通常能保留更多信息,对后续建模更有利。
- 动手实践:使用上面提供的等距和等频离散化代码,尝试将桶数
n_bins改为5,观察桶边界的变化。然后,假设有一个新样本Fare=200.0,请手动判断它分别会落入等距和等频的哪个桶中?
👉 点击查看参考答案
import numpy as np
from sklearn.preprocessing import KBinsDiscretizer
# 使用同样的数据
sample_fare = [[200.0]]
# 等距5桶
enc_width_5 = KBinsDiscretizer(n_bins=5, encode='ordinal', strategy='uniform')
enc_width_5.fit([[0.0], [512.0]]) # 用最小最大值拟合
print("等距5桶边界:", enc_width_5.bin_edges_)
print("新样本 Fare=200.0 等距切分落入桶:", enc_width_5.transform(sample_fare).astype(int)[0][0])
# 等频5桶 (需要用到完整数据分布来拟合,这里用之前10个样本的数据分布来近似)
data_for_freq = np.array([0.0, 7.5, 8.7, 26.0, 512.0, 5.09, 1.8, 60.34, 27.57, 36.94]).reshape(-1,1)
enc_freq_5 = KBinsDiscretizer(n_bins=5, encode='ordinal', strategy='quantile')
enc_freq_5.fit(data_for_freq)
print("\n等频5桶边界:", enc_freq_5.bin_edges_)
print("新样本 Fare=200.0 等频切分落入桶:", enc_freq_5.transform(sample_fare).astype(int)[0][0])等距5桶边界: [array([ 0. , 102.4, 204.8, 307.2, 409.6, 512. ])]
新样本 Fare=200.0 等距切分落入桶: 1
等频5桶边界: [array([ 0. , 3.445, 8.1 , 26.785, 48.64 , 512. ])]
新样本 Fare=200.0 等频切分落入桶: 4根据计算(或运行上述代码),在10个样本的分布下,等频切分的第5个桶边界约为(48.64, 512.0],200.0落在这个桶内(编号4)。等距切分下,200.0落在第二个桶[102.4, 204.8)内(编号1)。请注意,等频的边界严重依赖于拟合所用的数据分布。
本章小结
本节我们深入学习了特征工程中的一项关键技术——特征离散化。我们理解了它如何通过将连续值分段来增强模型能力,并掌握了从简单的无监督方法(等距、等频、K-Means)到更复杂的有监督方法(决策树、卡方合并)的完整工具箱。
📋 行动清单
- 尝试对比:在同一个数据集上,对某个连续特征分别用等频和基于决策树的方法进行离散化,观察生成的新特征与标签的相关性有何不同。
- 流程巩固:严格遵循“在训练集
fit,在训练集和测试集分别transform”的流程,编写一个完整的离散化处理函数。 - 思考延伸:离散化后的桶编号是顺序变量,直接输入线性模型是否合适?如果不合适,下一步应该做什么处理?(提示:回顾类别型特征编码)
—— 小象教研组