等距离散法
将连续数据的范围划分为几个宽度相等的区间。每个区间内的数值都有相同的范围。
1 2 3 4 5 6 7 8 9 10 11
| import pandas as pd import numpy as np
data = np.random.randn(1000)
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)
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)
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
data = np.random.randn(1000)
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
data = np.random.randn(1000)
percentiles = np.percentile(data, q=[10, 20, 30, 40, 50, 60, 70, 80, 90])
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)
optb = OptimalBinning(name="feature", dtype="numerical", solver="cp") optb.fit(data, target)
bin_edges = optb.splits print("自动计算的分箱边界:", bin_edges)
|
✅ 优点
保持类别信息:确保离散化后不同类别仍然可以区分,提高模型效果。
自动确定最优分箱:基于卡方值合并区间,减少信息损失。
避免过度离散化:不像等宽分箱或等频分箱可能导致信息丢失。
❌ 缺点
计算量较大:随着样本数增加,计算卡方统计量的复杂度会增加。
依赖类别变量:只能用于分类任务,如果目标变量是连续值,需要先离散化。
需要调整超参数:最大分箱数 max_bins 需要根据数据调优。
分类任务:目标变量是离散类别(如二分类、多分类)。
决策树模型:离散化后可以提升决策树模型的可解释性。
数据量较大:对于大规模数据,可通过 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)
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)
discretizer = KBinsDiscretizer(n_bins=4, encode='ordinal', strategy='uniform') binned_data = discretizer.fit_transform(data)
print("1R 近似分箱结果:", np.unique(binned_data))
|
✅ 优点
监督式分箱:保留目标变量信息,减少信息损失。
简单易懂:规则明确,适合初步数据探索。
适用于分类任务:尤其适用于决策树、朴素贝叶斯等分类模型。
❌ 缺点
计算复杂度较高:需要尝试多个分箱方案,计算错误率。
可能过拟合:如果 max_bins 过大,可能导致分箱过多,导致模型过拟合。
仅适用于分类任务:如果目标变量是连续值,需要先进行离散化。
分类任务(目标变量是离散类别,如 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
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)
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) 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
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 格式)。
❌ 不适用场景
信息损失严重:如果数据本身具有重要的连续性信息(如温度、房价),二值化可能会损失过多信息。
非布尔场景:如果数据有多个类别,建议使用多级离散化(如分箱)。