连续特征离散化的处理方法及其python实现

姜智浩 Lv4

等距离散法

将连续数据的范围划分为几个宽度相等的区间。每个区间内的数值都有相同的范围。

1
2
3
4
5
6
7
8
9
10
11
import pandas as pd
import numpy as np

# 生成连续数据
data = np.random.randn(1000)

# 将数据分为 5 个等宽区间
bins = pd.cut(data, bins=5)

# 查看分箱结果
print(bins.value_counts())

等频离散法

将数据分成几个区间,使得每个区间内的数据量相同。这样可以避免某些区间的数据过于集中。

1
2
3
4
5
6
7
8
9
10
11
import pandas as pd
import numpy as np

# 生成连续数据
data = np.random.randn(1000)

# 将数据分为 5 个等频区间
bins = pd.qcut(data, q=5)

# 查看分箱结果
print(bins.value_counts())

K-means 模型离散法

先从样本集中随机选取 k个样本作为簇中心,并计算所有样本与这 k个“簇中心”的距离,对于每一个样本,将其划分到与其距离最近的“簇中心”所在的簇中。

1
2
3
4
5
6
7
8
9
10
11
12
13
import pandas as pd
import numpy as np
from sklearn.cluster import KMeans

# 生成连续数据
data = np.random.randn(1000).reshape(-1, 1)

# 使用 K-means 聚类
kmeans = KMeans(n_clusters=5)
labels = kmeans.fit_predict(data)

# 查看聚类标签
print(pd.Series(labels).value_counts())

基于决策树的离散化

基于决策树的方法利用决策树的分割规则进行离散化,将连续特征分割成多个区间,通常用于有监督学习中的离散化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import pandas as pd
import numpy as np
from sklearn.tree import DecisionTreeClassifier

# 生成连续数据
data = np.random.randn(1000)
target = np.random.randint(0, 2, size=1000) # 假设目标变量是二分类

# 将连续数据转化为二维数组
X = data.reshape(-1, 1)

# 使用决策树进行离散化
clf = DecisionTreeClassifier(max_leaf_nodes=5)
clf.fit(X, target)

# 获取每个样本所属的区间
labels = clf.apply(X)

# 查看离散化结果
print(pd.Series(labels).value_counts())

分位数离散法

分位数离散化的核心思想是:
按照数据的累积分布函数 (CDF) 计算分位点。
根据分位数(如四分位数 (quartiles)、十分位数 (deciles))划分数据,使得每个区间的样本数接近相等。
由于基于数据的分布进行划分,适用于非均匀分布的数据。

以下是不同的方法进行实现

使用pandas.qcut()

1
2
3
4
5
6
7
8
9
10
11
import pandas as pd
import numpy as np

# 生成 1000 个随机数据
data = np.random.randn(1000)

# 使用 qcut 进行分位数离散化(四分位)
bins = pd.qcut(data, q=4, labels=['Q1', 'Q2', 'Q3', 'Q4'])

# 统计每个分箱的数据量
print(bins.value_counts())

使用numpy.percentile()

1
2
3
4
5
6
7
8
9
10
11
12
13
import numpy as np

# 生成 1000 个随机数据
data = np.random.randn(1000)

# 计算 10 分位数(十分位数)
percentiles = np.percentile(data, q=[10, 20, 30, 40, 50, 60, 70, 80, 90])

# 使用 np.digitize 进行分箱
bins = np.digitize(data, percentiles)

# 统计每个分箱的数据量
print(np.bincount(bins))

分位数离散化的优缺点

✅ 优点
适用于非均匀分布数据,相比于等宽离散化更合理。
避免某些区间数据过多或过少,能够更好地均衡数据。
能够减少异常值的影响,因为分箱是基于数据分布,而不是固定范围。

❌ 缺点
对极端值敏感,如果数据中有极端值,可能会影响分位数计算结果。
区间边界难以解释,不像等宽分箱那样有固定的区间宽度。
对新数据可能需要重新计算分位点,导致难以适用于流式数据。

何时使用分位数离散化?

数据分布不均匀(例如数据集中在某些范围)。
避免某些区间样本过多或过少,如在决策树、统计建模等场景中使用。
希望减少异常值的影响,避免极端值导致不均匀的划分。

基于卡方分裂的离散法

该分裂算法是把整个属性的取值区间当做一个离散的属性值,然后对该区间进行划分,一般是一分为二,即把一个区间分为两个相邻的区间,每个区间对应一个离散的属性值,该划分可以一直进行下去,直到满足某种停止条件,其关键是划分点的选取。

方法一
手动实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import numpy as np
import pandas as pd
from scipy.stats import chi2_contingency

def chi2_value(freq_table):
"""计算卡方值"""
chi2, p, _, _ = chi2_contingency(freq_table)
return chi2

def chimerge(data, target, max_bins=5):
"""
基于卡方分裂的离散化方法(ChiMerge)
:param data: 连续特征(NumPy 数组或 Pandas Series)
:param target: 目标变量(分类变量)
:param max_bins: 期望的最大分箱数
:return: 分箱边界
"""
df = pd.DataFrame({'feature': data, 'target': target})
df = df.sort_values(by='feature').reset_index(drop=True) # 按特征值排序

# 统计每个特征值下的类别频次
freq_table = df.groupby('feature')['target'].value_counts().unstack().fillna(0)

# 初始边界(每个值单独作为一个区间)
bins = list(freq_table.index)

while len(bins) > max_bins: # 直到达到最大分箱数
min_chi2 = float('inf')
min_index = -1

# 遍历所有相邻区间,找到卡方值最小的区间
for i in range(len(bins) - 1):
merged_table = freq_table.loc[[bins[i], bins[i + 1]]].sum(axis=0).values.reshape(2, -1)
chi2 = chi2_value(merged_table)

if chi2 < min_chi2: # 找到最小的卡方值
min_chi2 = chi2
min_index = i

# 合并卡方值最小的区间
bins[min_index] = (bins[min_index] + bins[min_index + 1]) / 2
bins.pop(min_index + 1) # 删除合并的区间

return bins

# 示例数据
np.random.seed(42)
data = np.random.randn(100) * 10 + 50 # 生成随机数
target = np.random.choice([0, 1], size=100) # 二分类目标变量

# 计算分箱边界
bin_edges = chimerge(data, target, max_bins=4)
print("卡方分裂后的分箱边界:", bin_edges)

方法二
使用optbinning库

如果不想手动实现,可以使用 optbinning 库,它可以进行最优分箱(Optimal Binning),内部使用 ChiMerge 或者 Decision Tree 进行离散化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from optbinning import OptimalBinning

# 生成示例数据
np.random.seed(42)
data = np.random.randn(100) * 10 + 50
target = np.random.choice([0, 1], size=100)

# 使用 OptimalBinning 进行卡方离散化
optb = OptimalBinning(name="feature", dtype="numerical", solver="cp")
optb.fit(data, target)

# 获取分箱边界
bin_edges = optb.splits
print("自动计算的分箱边界:", bin_edges)

ChiMerge的优缺点

✅ 优点
保持类别信息:确保离散化后不同类别仍然可以区分,提高模型效果。
自动确定最优分箱:基于卡方值合并区间,减少信息损失。
避免过度离散化:不像等宽分箱或等频分箱可能导致信息丢失。

❌ 缺点
计算量较大:随着样本数增加,计算卡方统计量的复杂度会增加。
依赖类别变量:只能用于分类任务,如果目标变量是连续值,需要先离散化。
需要调整超参数:最大分箱数 max_bins 需要根据数据调优。

何时使用ChiMerge

分类任务:目标变量是离散类别(如二分类、多分类)。
决策树模型:离散化后可以提升决策树模型的可解释性。
数据量较大:对于大规模数据,可通过 optbinning 等库加速处理。

ChiMerge 离散化适用于分类任务,可以在决策树、朴素贝叶斯等模型中提升效果。对于回归任务,可以考虑 KMeans 或 等宽/等频分箱。

1R离散法

1R 就是 1-rule,称为1 规则,也就是产生一层的决策树,用一个规则集的形式,只在某个特定的属性上进行测试。1R是一个简单廉价的方法,但却常常能得到令人吃惊的准确率。
它的核心思想是:
将连续特征划分为多个区间,然后
寻找能够最好地预测目标变量(类别)的区间划分。

1R 方法的基本步骤如下:
对特征值排序。
尝试不同的分箱方法(等宽、等频、信息增益等),并计算分类错误率。
选择错误率最低的分箱方式作为最终的离散化方式。

方法一
手动实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import numpy as np
import pandas as pd

def one_r_discretization(data, target, max_bins=5):
"""
基于 1R 规则的离散化方法
:param data: 连续特征 (NumPy 数组或 Pandas Series)
:param target: 目标变量 (分类变量)
:param max_bins: 期望的最大分箱数
:return: 最优分箱边界
"""
df = pd.DataFrame({'feature': data, 'target': target})
df = df.sort_values(by='feature').reset_index(drop=True) # 按特征值排序

best_bins = None
best_error = float('inf')

# 尝试不同分箱方式
for bins in range(2, max_bins + 1):
df['bin'] = pd.cut(df['feature'], bins=bins, labels=False) # 进行分箱
bin_stats = df.groupby('bin')['target'].agg(lambda x: x.value_counts().index[0]) # 每个分箱内占比最高的类别

# 计算错误率
df['pred'] = df['bin'].map(bin_stats)
error_rate = (df['pred'] != df['target']).mean()

# 选择错误率最低的分箱方案
if error_rate < best_error:
best_error = error_rate
best_bins = df['bin'].unique()

return best_bins

# 示例数据
np.random.seed(42)
data = np.random.randn(100) * 10 + 50 # 生成随机数
target = np.random.choice([0, 1], size=100) # 目标变量(0 或 1)

# 计算分箱边界
bin_edges = one_r_discretization(data, target, max_bins=4)
print("1R 规则下的最佳分箱边界:", bin_edges)

方法二
使用KBinsDiscretizer

1
2
3
4
5
6
7
8
9
10
from sklearn.preprocessing import KBinsDiscretizer

# 生成示例数据
data = data.reshape(-1, 1) # 需要转换为 2D 数组

# 使用监督式分箱
discretizer = KBinsDiscretizer(n_bins=4, encode='ordinal', strategy='uniform')
binned_data = discretizer.fit_transform(data)

print("1R 近似分箱结果:", np.unique(binned_data))

1R方法的优缺点

✅ 优点
监督式分箱:保留目标变量信息,减少信息损失。
简单易懂:规则明确,适合初步数据探索。
适用于分类任务:尤其适用于决策树、朴素贝叶斯等分类模型。

❌ 缺点
计算复杂度较高:需要尝试多个分箱方案,计算错误率。
可能过拟合:如果 max_bins 过大,可能导致分箱过多,导致模型过拟合。
仅适用于分类任务:如果目标变量是连续值,需要先进行离散化。

何时使用1R离散化?

分类任务(目标变量是离散类别,如 0/1 或 A/B/C)。
数据探索:快速找到能够划分类别的最优分箱方式。
决策树建模:如 CART、ID3、C4.5 等模型。

二值化离散法

二值化离散法是一种简单且常用的离散化方法,它的基本思想是 将连续变量转换为两个类别(0 和 1),即:
小于某个阈值的设为 0
大于等于某个阈值的设为 1

这种方法特别适用于需要转换成 布尔值(Boolean) 的场景,如 信用评分、风险预测 或 神经网络中的二元特征输入。

方法一
手动二值化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 import numpy as np
import pandas as pd

# 生成示例数据
np.random.seed(42)
data = np.random.randn(10) * 10 + 50 # 生成 10 个均值为 50 的随机数

# 固定阈值(如50)
threshold = 50
binarized_data_fixed = (data >= threshold).astype(int)

# 均值二值化
threshold_mean = np.mean(data)
binarized_data_mean = (data >= threshold_mean).astype(int)

# 中位数二值化
threshold_median = np.median(data)
binarized_data_median = (data >= threshold_median).astype(int)

# 75% 分位数二值化
threshold_percentile = np.percentile(data, 75)
binarized_data_percentile = (data >= threshold_percentile).astype(int)

# 输出结果
df = pd.DataFrame({
'Original Data': data,
'Fixed Threshold': binarized_data_fixed,
'Mean Threshold': binarized_data_mean,
'Median Threshold': binarized_data_median,
'Percentile Threshold': binarized_data_percentile
})
print(df)

方法二
使用 sklearn 进行二值化

1
2
3
4
5
6
7
from sklearn.preprocessing import Binarizer

# 设定固定阈值
binarizer = Binarizer(threshold=50) # 以50为阈值
binarized_data = binarizer.fit_transform(data.reshape(-1, 1))

print("二值化后的数据:\n", binarized_data.flatten())

方法三
监督式二值化(基于决策树)

如果有分类标签 Y,可以使用决策树来学习最佳的二值化阈值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from sklearn.tree import DecisionTreeClassifier

# 假设目标变量 Y
target = np.random.choice([0, 1], size=len(data))

# 训练决策树
tree = DecisionTreeClassifier(max_depth=1) # 只允许一层分裂
tree.fit(data.reshape(-1, 1), target)

# 获取最优的分裂阈值
optimal_threshold = tree.tree_.threshold[0]
binarized_data_tree = (data >= optimal_threshold).astype(int)

print(f"决策树选择的最优二值化阈值: {optimal_threshold}")
print("基于决策树的二值化结果:", binarized_data_tree)

何时使用二值化?

✅ 适用场景
逻辑回归或朴素贝叶斯模型(需要布尔特征)。
信用评分、欺诈检测(例如:收入是否高于某个值?)。
生物信息学(例如:基因表达水平是否超过某个阈值?)。
特征筛选(减少噪声,提高模型可解释性)。
规则挖掘(如 Apriori 算法)(将数据转换为 0/1 格式)。

❌ 不适用场景
信息损失严重:如果数据本身具有重要的连续性信息(如温度、房价),二值化可能会损失过多信息。
非布尔场景:如果数据有多个类别,建议使用多级离散化(如分箱)。

  • Title: 连续特征离散化的处理方法及其python实现
  • Author: 姜智浩
  • Created at : 2025-03-29 11:45:14
  • Updated at : 2025-03-31 20:01:48
  • Link: https://super-213.github.io/zhihaojiang.github.io/2025/03/29/20250329连续特征离散化的处理方法及其python实现/
  • License: This work is licensed under CC BY-NC-SA 4.0.