之前的决策树分类部分只完成了对离散值的分类,当遇到有某些属性值是连续的时候就需要一些其他的方法了。
由于连续值的可取值数目不再有限,因此不能直接根据连续属性的可取值来对节点进行划分。此时就需要连续属性的离散化,最简单的策略是采用二分法对连续属性进行处理,这正是C4.5决策树算法中采用的机制。下面我们以周志华老师的西瓜书为例画一个带有连续值分类的决策树。
给定样本集D和连续属性a,假定a在D上出现了n个不同的取值,将这些值从小到大进行排序,记为:{a1,a2,...,an}。基于划分点t可将D划分为子集Dt-和Dt+,其中Dt-包含那些属性a上取值不大于t的样本,而Dt+则包含那些属性a上取值大于t的样本。显然,对相邻的属性 ai 与 ai+1 来说t在区间[ai, ai+1)中取任意值所产生的划分结果相同。因此对连续属性a,我们可考察包含n-1个元素的候选划分点集合:
结合之前我们所使用的划分离散值的决策树编写对连续值进行划分的代码:
caldataSet = np.array(dataSet) Num_density = caldataSet[:,6] Num_sugar = caldataSet[:,7] #密度和含糖度连续值排序 density_sort = sorted(Num_density) sugar_sort = sorted(Num_sugar) nd = len(Num_density) - 1 ns = len(Num_sugar) - 1 #密度的划分点 T_density = [] for i in range(nd): t = (float(density_sort[i])+float(density_sort[i+1]))/2.0 T_density.append(t) #含糖率的划分点 T_sugar = [] for i in range(ns): t = (float(sugar_sort[i])+float(sugar_sort[i+1]))/2.0 T_sugar.append(t)
#提取类标签 L_Label = [] for value in dataSet: L_Label.append(value[8]) L_Label = np.array(L_Label)
首先是将数据集中的连续值部分提取出来进行排序;第二段将每个连续值对应的标签提取出来,用于与离散后的属性值组合在一起当作输入数据集使用。
接下来将数据集中的连续值分别与划分点进行比较进行离散化:
#按照密度划分点划分后的样本列表 copy_Num_density = Num_density.copy() #print(id(Num_density),id(copy_Num_density)) density_list = [] for i in range(nd): for j in range(len(Num_density)): #比较划分点和各个数值的大小 if float(Num_density[j])<float(T_density[i]): copy_Num_density[j] = 0 else: copy_Num_density[j] = 1 copy_Num_density_list = copy_Num_density.tolist() density_list.append(copy_Num_density_list) #按照含糖率划分后的样本列表 copy_Num_sugar = Num_sugar.copy() sugar_list = [] for i in range(ns): for j in range(len(Num_sugar)): #比较划分点和各个数值的大小 if float(Num_sugar[j])<float(T_sugar[i]): copy_Num_sugar[j] = 0 else: copy_Num_sugar[j] = 1 copy_Num_sugar_list = copy_Num_sugar.tolist() sugar_list.append(copy_Num_sugar_list)
接下来比较每次离散化后的样本的信息增益选取最好的连续属性离散值:
#选取最优的离散化连续值序列 def chooseBestFeatureinScatter(dataSet): ''' 输入数据集:1、由列表元素组成的列表, 2、列表的最后一列是当前实例的标签 ''' #计算数据集的香浓熵,用于与划分之后的香浓熵进行比较 baseEntropy = calcShannonEnt(dataSet) bestInfoGain = 0.0 ''' 取出每一个特征的所有特征值,直至取完所有的特征值 按照每个特征值对数据集进行分类 计算每次分类后的香浓熵 ''' featList = [example[0] for example in dataSet] #去除重复的元素,留下的是不重复的特征能取得的值 uniqueVals = set(featList) newEntropy = 0.0 #遍历每一个特征值 for value in uniqueVals: #取出的是第i个特征,对应的特征值为value subDataSet = splitDataSet(dataSet, 0, value) #计算信息增益 prob = len(subDataSet)/float(len(dataSet)) newEntropy += prob*calcShannonEnt(subDataSet) infoGain = baseEntropy - newEntropy return infoGain #密度和标签构成 17*2的列表 input_density = [] a = [] shannotEnt_a = [] i_a = 0 M_shannonEnt = 0 for value in density_list: input_density = np.array(value) input_density.reshape(17,1) #print(input_density.shape) a = np.column_stack((input_density,L_Label)) a = a.tolist() shannotEnt_a.append(chooseBestFeatureinScatter(a)) M_shannonEnt = max(shannotEnt_a) i_a = shannotEnt_a.index(M_shannonEnt) Best_density = density_list[i_a] #含糖率和标签构成 17*2的列表 input_sugar = [] b = [] shannotEnt_b = [] i_b = 0 M_shannonEnt = 0 for value in sugar_list: input_sugar = np.array(value) input_sugar.reshape(17,1) b = np.column_stack((input_sugar,L_Label)) b = b.tolist() shannotEnt_b.append(chooseBestFeatureinScatter(b)) M_shannonEnt = max(shannotEnt_b) i_b = shannotEnt_b.index(M_shannonEnt) Best_sugar = sugar_list[i_b] #输入样本处理: caldataSet[:,6] = Best_density caldataSet[:,7] = Best_sugar dataSet = caldataSet.tolist()
这里我的处理有一些问题,按照C4.5决策树算法的机制,在父节点使用了连续属性进行划分之后,该属性还可以作为其后代节节点的划分属性。应该是每次划分决策树节点时都要选取最优的连续属性值分类并且和其他的属性进行比较选取信息增益最大的点,应该有一个递归比较的过程,这里我打印了所有连续属性的划分增益,发现连续属性中除了最佳划分点划分后的数据集之外没有比离散属性更好的划分点了,所以我就没有使用递归比较,这里我也没有想好怎么写,等以后有更好的方法我再回来补上。
将离散后的属性值代替之前的连续值,作为输入样本得到决策树为:
画出图:
这里纹理和触感的属性值在数据集里为汉字,没法显示出来,改成相对应的英文即可。