目录
1 什么是特征分箱
2 分箱的重要性及其优势
3 有监督分箱
3.1卡方分箱原理
3.2 决策树分箱
4 无监督分箱
4.1 等距分箱
4.2 等频分箱
4.3 分位数分箱
实验数据:链接: 提取码:czum
实验数据介绍:参考文章1.2节(数据介绍链接)
1 什么是特征分箱
数据的一种预处理技术, 是特征分箱, 它主要用于把连续特征, 或者名为密集离散特征的这类特征, 转变为离散特征呈现, 如此这般能够提升模型的性能。
目标: 特征分箱要达成的是, 把连续类型(或者是密集类型的离散情况)变量所具备的值范围, 划分成好多不同的区间(也就是所谓的"箱子"), 接着还得把这些划定好的区间, 对应映射到离散的类别或者标签上去。
连续变量或者密集离散变量中, 可能存在着非线性关系或是复杂的模式, 而这些模式呢, 对于某些机器学习算法来讲, 想要捕捉是比较困难的。所以分箱就出现了作用了, 借助分箱, 我们能够专门把这些复杂的连续变量, 或者是密集离散变量, 简化为那种更方便模型去做处理的离散变量。
例如,我们有一组关于人年龄的数据,如下图所示:

现在有着这样的期望, 期望将他们的年龄进行分组, 分组进入更少的间隔当中, 而要达成这个目的, 是能够借助设置一些条件来予以实现的:

2 分箱的重要性及其优势
一般而言,于构建分类模型之际, 所需进行的是对连续变量予以离散化处理, 当特征完成离散化操作之后, 此时的模型会更具稳定性, 且呈现出降低了模型过拟合风险的状况。分箱具备如下这般的重要性以及优势:
十分易于迭代, 离散化特征的增添进而减少极其便利 , 利于模型迅速迭代优化。具备计算效率 , 稀疏向量内积乘法运算效率极高 , 得出的结果存储便利 、轻松可扩展。强劲的鲁棒性 , 离散化后的特征针对异常数据怀有很强的鲁棒性。比方说 , 某个特征是年龄大于30便为1 , 否则是0。要是特征未曾离散化 , 一个少见的数据 "年龄120岁 "会给模型带来极大干扰。关于特征交叉 , 离散化以后能够进行特征交叉 , 进一步增添非线性 , 提升模型的表达能力。简化模型, 以便降低过拟合风险, 与此同时允许把缺失值视作独立类别, 进而统一变量尺度。3有监督分箱, 3.1是卡方分箱原理。
卡方分箱概念
卡方分箱是种基于统计学原理特征离散化方法, 它依靠卡方检验评估相邻区间类分布是否相似, 若两个连续相邻区间类分布相似, 那么就能合并这些区间, 要是不同, 那就得保持分开, 卡方分箱基本思想在于精确离散化里, 一个区间内相对类频率应该完全一样。
卡方分布
为了能更优地领会卡方分箱, 我们先瞧一瞧关于卡方分布, 用于概率论跟统计学经常会用到的一种概率分布, 也是统计推断里应用极为广泛的概率分布当中的一个, 卡方分布的定义是依据标准正态分布, 它的数学定义以下这般。
要是存在k个彼此独立的随机变量, 它们分别是Z1、Z2、......、Zk, 并且均满足标准正态分布N(0, 1), 那么这k个随机变量那所谓平方和哟啥玩意儿呢。
服从自由度为k的卡方分布,记作:
卡方检验
卡方检验是一种依卡方分布为根基的 , 假设检验方法。其主要作用在于 , 比较观察值跟期望值之间 , 是否存有差异。此方法格外适用于分类数据 , 像性别 、教育水平这类。
其首要思想在于依据样本给出的数据, 去推断总体的具体分布跟期望分布是不是存在显著的差异, 又或是凭借这些数据推断两个分类性质的变量是不是具备相关性或者相互独立的特性。通常情况下能够设定原假设为这样的情形: 观测得到的频数与预期的频数之间不存在差异, 也就是两个变量彼此相互独立不存在关联性, 换而言之就是相关因素不会对目标变量产生影响。
真实运用里, 我们率先假定原假设是成立的, 进而算出卡方值, 卡方值所依循的计算公式是:
其中,A为实际频数,E为期望频数。
通过该假设, 计算得出卡方值, 此卡方值用来表明观察值跟理论值之间的偏离程度, 依据卡方分布以及自由度, 能够确定在原假设成立的情形下, 获取当前统计量以及更极端情况的概率P。

其中, 第一列所对应的是自由度, 在红框之中的对应的则是P值, 不同的自由度以及P值所对应的是卡方值。能够觉察到, 在相同的自由度情况之下, 卡方值倘若越大P值就越小。反过来, 要是P值越小, 那么卡方值就越大, 这表明, 观察值与理论值之间存在太大程度的偏离, 原本的假设应当被拒绝。
卡方分箱的基本思想是关于精确离散化一方面在于让相对类频率在一个区间内完全保持一致。因而另一方面如果有两个相邻的区间其类分布极为近似, 则这两个区间能够进行合并, 不然的话, 它们就应该维持分开的状态。
卡方分箱步骤
(1) 初步进行分箱操作, 把那连续变量所具有的值范围划分成好多初始的区间, 也就是所谓的箱子。这种划分能够依据等宽的方式, 即每个箱子的取值范围是一样的, 或者依据等频的方式, 就是每个箱子里的样本数量大体上是相同的。
(2) 去计算卡方统计量, 要运用以下公式来计算卡方统计量, 借其评估箱子之间的独立性,哎!
其中,A为实际频数,E为期望频数。
(3) 选择箱子对来合并, 选中具有最小卡方统计量的箱子对, 对于每一对相邻箱子,若它们卡方统计量小于某个基于p值出来的阈值, 那就认为这两个箱子在目标变量的分布上面没有显著差异, 所以可以合并。
(4) 迭代的过程是, 要不间断地重复进行步骤2, 然后持续重复步骤3, 直至满足终止方面的条件。就犹如达到预先规定好的箱子数量, 或者是已经无法得以进一步合并那样才停止。
3.2 决策树分箱
决策树分箱会这么完成进行, 它会去评估不一样的分割点, 以此来寻觅最优的分箱边界, 进而让每个箱内的数据在目标变量方面尽可能地呈现同质状态。借由这种办法, 决策树能够学到数据里面的复杂模式, 并且在分类或者回归任务里提升模型的预测准确性。
在决策树分箱进程里, 算法会逐个遍历数据集中的全部特征,且尝试于每个特征之上开展分割, 借此来降低节点的不纯度, 不纯度的衡量能够依据信息熵或者基尼不纯度等指标作出, 算法会递归性地挑选最优的分割点, 直至满足停止条件, 像是达成预先设定的最大树深度, 不然就是节点里头的样本数量低于某个阈值, 这种方式不但提升了模型的泛化能力, 还强化了模型的可解释性, 原因在于分箱之后的特征更容易被人类理解以及解释。
4 无监督分箱4.1 等距分箱
有一种简单的分箱技术, 叫等距分箱(Equal-width), 它会把数据范围划分成若干个等宽的区间, 等距分箱常常被用于数据可视化以及预处理, 特别是在数据分布相对均匀的时候。
python
import pandas as pd
def equal_width_binning(df, column_name, num_bins, print_info=True):
"""
对指定的列进行等距分箱,并可选择打印分箱信息。
参数:
df : pd.DataFrame,输入的DataFrame。
column_name : str,要分箱的列名。
num_bins : int,箱的数量。
print_info : bool, optional,是否打印分箱信息,默认为True。
返回:
pd.DataFrame
包含原始数据和分箱结果的新DataFrame。
"""
# 检查列是否存在于DataFrame中
if column_name not in df.columns:
raise ValueError(f"Column '{column_name}' does not exist in the DataFrame.")
# 检查箱的数量是否为正数
if num_bins <= 0:
raise ValueError("Number of bins must be a positive integer.")
# 计算箱宽
bin_width = (df[column_name].max() - df[column_name].min()) / num_bins
# 创建箱边界
bins = [df[column_name].min() + i * bin_width for i in range(num_bins + 1)]
# 打印分箱信息
if print_info:
print(f"Column: {column_name}")
print(f"Number of bins: {num_bins}")
print(f"Bin width: {bin_width:.2f}")
print("Bin edges:")
print(bins)
# 将数据分到箱中
binned_column_name = f"{column_name}_binned"
df[binned_column_name] = pd.cut(df[column_name], bins=bins, right=True)
return df
python
# 示例用法
# 假设df是您的DataFrame,'feature_column'是您要分箱的特征列名
df_binned = equal_width_binning(data, 'Age', num_bins=5)
df_binned
对数据的'Age'进行等距分段,运行的结果如下:

4.2 等频分箱
有一种技术, 叫作等频分箱(Equal-), 这是将数据进行分隔的方法, 分隔成的数据箱里, 数据点的数量是相同的, 或者几乎是相同的。这种方法能确保的是, 每个箱里面的数据点数量, 大致是相等的, 并非是箱的大小, 也就是宽度或者范围相等。当数据的分布不均匀的时候, 或者存在极端值的时候, 等频分箱就特别有用了。
python
import pandas as pd
def equal_frequency_binning(df, column_name, num_bins, print_info=True):
"""
对指定的列进行等频分箱,并可选择打印分箱信息。
参数:
df : pd.DataFrame,输入的DataFrame。
column_name : str,要分箱的列名。
num_bins : int,箱的数量。
print_info : bool, optional,是否打印分箱信息,默认为True。
返回:pd.DataFrame,包含原始数据和分箱结果的新DataFrame。
"""
# 参数检查
if column_name not in df.columns:
raise ValueError(f"Column '{column_name}' does not exist in the DataFrame.")
if num_bins <= 0:
raise ValueError("Number of bins must be a positive integer.")
# 对数据进行排序
df_sorted = df.sort_values(by=column_name)
# 计算每个箱的大致数据点数量
bin_size = len(df) // num_bins
# 确定箱边界
bins = [df_sorted[column_name].min()] # 包括最小值
for i in range(1, num_bins):
bin_bound = df_sorted.iloc[i * bin_size - 1][column_name]
bins.append(bin_bound) # 包括最大值
bins.append(df_sorted[column_name].max()) # 包括最大值
# 打印分箱信息
if print_info:
print(f"Column: {column_name}")
print(f"Number of bins: {num_bins}")
print("Bin edges:")
for i, bin_edge in enumerate(bins[:-1]):
print(f"Bin {i}: x <= {bin_edge}")
print(f"Bin {num_bins - 1}: x > {bins[-2]}")
print("Bin centers and counts:")
for i in range(num_bins):
center = df_sorted.iloc[(i * bin_size + (i + 1) * bin_size) // 2][column_name]
count = len(df_sorted[(df_sorted[column_name] > bins[i]) & (df_sorted[column_name] <= bins[i + 1])])
print(f"Bin {i} center: {center}, count: {count}")
# 将数据分到箱中
df[f'{column_name}_binned'] = pd.cut(df[column_name], bins=bins, right=True, labels=range(num_bins))
return df
python
# 示例用法
# 假设df是您的DataFrame,'feature_column'是要分箱的特征列名
df_binned = equal_frequency_binning(data, 'Age', num_bins=5)
df_binned
把数据之中的'Age'开展等距分段, 运行得来的结果如下, 能够看到不同区间具有大致同样多的数量。

4.3 分位数分箱
将分位数分布进行分箱操作, 这是一种依靠数据既有分布情况的分箱技术, 它凭借数据所呈现的分位数去明确分箱形成的边界所在之处。这样的办法有助于保证每个箱子内部的数据点有着相近的累积发生概率, 进而让各个箱子在数理统计特征方面趋于相似。对于解析存在不均衡不对称分布态势的数据而言 按分位数进行分箱是极为实用的。
python
import pandas as pd
def quantile_binning(df, column_name, num_bins, print_info=True):
"""
对指定的列进行分位数分箱,并可选择打印分位数信息。
参数:
df : pd.DataFrame,输入的DataFrame。
column_name : str,要分箱的列名。
num_bins : int,箱的数量。
print_info : bool, optional,是否打印分位数信息,默认为True。
返回:pd.DataFrame,包含原始数据和分箱结果的新DataFrame。
"""
# 参数检查
if column_name not in df.columns:
raise ValueError(f"Column '{column_name}' does not exist in the DataFrame.")
if num_bins <= 1:
raise ValueError("Number of bins must be greater than 1.")
# 计算分位数
quantiles = df[column_name].quantile([(i + 1) / num_bins for i in range(num_bins - 1)])
# 确定箱边界
bins = [df[column_name].min()] + quantiles.tolist() + [df[column_name].max()]
# 打印分位数信息
if print_info:
print(f"Column: {column_name}")
print(f"Number of bins: {num_bins}")
print("Quantile cuts:")
for i, quantile in enumerate(quantiles, start=1):
print(f"Quantile {i}/{num_bins}: {quantile}")
# 将数据分到箱中
df[f'{column_name}_binned'] = pd.cut(df[column_name], bins=bins, right=True, labels=range(num_bins))
return df
python
# 示例用法
# 假设df是您的DataFrame,'feature_column'是您要分箱的特征列名
df_binned = quantile_binning(data, 'Age', num_bins=5)
对'Age'这个数据, 采取等距分段的方式, 运行之后, 得出这样的结果 , 从中能够看到, 于不同分位数情形下的分段点。
