利用聚类离散化
聚类分析是一种流行的离散化方法。通过聚类算法(K-Means算法)将连续属性值进行聚类,处理聚类之后的到的k个簇。聚类考虑数据属性值的分布以及数据点的邻近性,因此可以产生高质量的离散化结果。
import numpy as np import pandas as pd from sklearn.cluster import KMeans small_counts = np.random.randint(0, 10000, 20) df = pd.DataFrame() df['amount'] = small_counts data_reshape = small_counts.reshape((data.shape[0],1)) model_kmeans = KMeans(n_clusters=4,random_state=0) kmeans_result = model_kmeans.fit_predict(data_reshape) df['amount_cut'] = kmeans_result df.sort_values(by=['amount_cut']) 复制代码
运行结果:
amount | amount_cut | |
9 | 9449 | 0 |
16 | 8283 | 0 |
15 | 7755 | 0 |
14 | 8226 | 0 |
13 | 9257 | 0 |
12 | 7068 | 0 |
7 | 7658 | 0 |
19 | 8728 | 0 |
4 | 2017 | 1 |
3 | 2956 | 1 |
2 | 3310 | 1 |
10 | 4500 | 2 |
11 | 4029 | 2 |
5 | 4936 | 2 |
17 | 5337 | 2 |
6 | 3971 | 2 |
8 | 432 | 3 |
18 | 208 | 3 |
1 | 1023 | 3 |
0 | 327 | 3 |
信息熵分箱
上面介绍的距离分箱和等频分箱方法对建模的优化有限。如果分箱后箱内样本对y的区分度好,那么这是一个好的分箱。通过信息论理论,我们可知,信息熵衡量了这种区分能力。当特征按照某个分隔点划分为上下两部分后能达到最大的信息增益,那么这就是一个好的分隔点。由上可知,信息熵分箱是有监督的分箱方法。其实,决策树的节点分裂原理也是基于信息熵。
它基本思想是离散后输入变量对输出变量的解释能力变强,则这种离散是有用的,否则是没有意义的。
它是利用信息增益最大化的方法寻找连续变量的最优切点,当切点确定后,将连续变量一分为二,分为两部分数据集,在这两部分数据集中用同样的方法循环切分,直到信息增益的值小于停止标准为止。
关于熵、信息、信息熵的说明:
熵的概念最早起源于物理学,用于度量一个热力学系统的无序程度。在信息论里面,熵是对不确定性的测量。
信息是用来消除随机不确定性的东西。
信息熵则是用来解决信息的度量问题。从公式上来看,它其实是一个随机变量信息量的数学期望。
基于决策树分箱
由于决策树的结点选择和划分也是根据信息熵来计算的,因此我们其实可以利用决策树算法来进行特征选择。
决策树分箱的原理就是用想要离散化的变量(单变量)用树模型拟合目标变量,例如,直接使用sklearn提供的决策树(是用cart决策树实现的),然后将内部节点的阈值作为分箱的切点。
这里以乳腺癌数据为例,首先取其中mean radius
字段,和target
字段来拟合一棵决策树。
import pandas as pd from sklearn.datasets import load_breast_cancer from sklearn.tree import DecisionTreeClassifier import numpy as np # bc = load_breast_cancer() # df = pd.DataFrame.from_records(data=bc.data, columns=bc.feature_names) # df['target'] = bc.target print(df.sample(5)[["mean radius","target"]]) print("-----------------") dt = DecisionTreeClassifier(criterion='entropy', max_depth=3) # 树最大深度为3 dt.fit(df['mean radius'].values.reshape(-1, 1), df['target']) print(dt.tree_.feature) # 存放每个节点所采用的特征 print(dt.tree_.threshold) # 存放每个节点特征的阈值 print(dt.tree_.children_left) # 存放经过特征和阈值分裂之后的左孩子 print(dt.tree_.children_right) # 存放经过特征和阈值分裂之后右孩子 print("-----------------") # 取出这课决策树的所有叶节点的分割点的阈值 print(np.where(dt.tree_.children_right > -1)) qtsr = dt.tree_.threshold[np.where(dt.tree_.children_right > -1)] print(qtsr) print(np.where(dt.tree_.children_left > -1)) qts = dt.tree_.threshold[np.where(dt.tree_.children_left > -1)] print(qts) print("-----------------") qts = np.sort(qts) res = [np.round(x, 3) for x in qts.tolist()] print(res) 复制代码
运行结果:
mean radius target 556 10.16 1 254 19.45 0 117 14.87 0 65 14.78 0 568 7.76 1 ----------------- [ 0 0 0 -2 -2 0 -2 -2 0 0 -2 -2 -2] [15.04500008 13.09499979 10.94499969 -2. -2. 13.70499992 -2. -2. 17.88000011 17.80000019 -2. -2. -2. ] [ 1 2 3 -1 -1 6 -1 -1 9 10 -1 -1 -1] [ 8 5 4 -1 -1 7 -1 -1 12 11 -1 -1 -1] ----------------- (array([0, 1, 2, 5, 8, 9]),) [15.04500008 13.09499979 10.94499969 13.70499992 17.88000011 17.80000019] (array([0, 1, 2, 5, 8, 9]),) [15.04500008 13.09499979 10.94499969 13.70499992 17.88000011 17.80000019] ----------------- [10.945, 13.095, 13.705, 15.045, 17.8, 17.88] 复制代码
注意:这里只给出了6个点,但是相当于分了7个箱子。
可视化:
我们将这7个箱子分别设为a-g,我们可以将划分后的效果绘制出来:
radius = df['mean radius'].values.tolist() r = [] for i in radius: if i < res[0]: r.append('a') elif i >= res[-1]: r.append('g') else: for j in range(0, 5): if i > res[j] and i <= res[j+1]: r.append(chr(98+j)) break sns.countplot(r) 复制代码
卡方分箱(ChiMerge)
卡方分箱是自底向上的(即基于合并的)数据离散化方法。它依赖于卡方检验:具有最小卡方值的相邻区间合并在一起,直到满足确定的停止准则。
关于卡方检验说明:
卡方检验可以用来评估两个分布的相似性,因此可以将这个特性用到数据分箱的过程中。
它主要包括两个阶段:初始化阶段和自底向上的合并阶段。
1、初始化阶段:
首先按照属性值的大小进行排序(对于非连续特征,需要先做数值转换,然后排序),然后每个属性值单独作为一组。
2、合并阶段:
(1)对每一对相邻的组,计算卡方值。
(2)根据计算的卡方值,对其中最小的一对邻组合并为一组。
(3)不断重复(1),(2)直到计算出的卡方值都不低于事先设定的阈值或者分组数达到一定的条件(如,最小分组数5,最大分组数8)。
简单来讲,卡方分箱就是不断的计算相邻区间的卡方值(卡方值越小表示分布越相似),将分布相似的区间(卡方值最小的)进行合并,直到相邻区间的分布不同,达到一个理想的分箱结果。理想的分箱是在同一个区间内标签的分布是相同的。