机器学习——决策树之连续值处理
一、连续属性离散化
连续属性就是像:0.697、0.774、0.634等的数字。 连续属性取值数目非有限,无法直接进行划分。这里采用二分法对连续属性进行处理。
我的数据集
第一步
将连续属性a在样本集D上出现的n个不同的取值从小到大排列,记为。基于划分点t,可将D分为子集
和
,其中
包含那些在属性a上取值不大于t的样本,
包含那些在属性a上取值大于t的样本。考虑包含n-1个元素的候选划分点集合
即把区间[)的中位点
作为 候选划分点
将连续属性绩点在样本集D上出现的17个不同的取值从小到大排序
2.801 2.998 3.169 3.184 3.190 3.318 3.373 3.425 3.462
3.789 3.893 4.009 4.151 4.238 4.312 4.413 4.423
计算候选划分点集合
2.900 3.083 3.176 3.187 3.254 3.346 3.399 3.444
3.630 3.845 3.951 4.080 4.194 4.275 4.363 4.418
第二步
采用离散属性值方法,计算这些划分点的增益,选取最优的划分点进行样本集合的划分 :

其中Gain(D,a,t)是样本集D基于划分点t二分后的信息增益,于是,就可以选择使Gain(D,a,t)最大化的划分点。
对于上面的数据集,有8个推荐,9个不推荐,信息熵为
对属性绩点,取划分点,则:
,其中推荐6个,不推荐2个
,其中推荐2个,不推荐7个
计算每个划分点的信息增益,得到最大信息增益为:0.366,对应划分点为3.346
与离散属性不同,若当前结点划分属性为连续属性,该属性还可以作为其后代结点的划分属性
二、代码实现
这里只展现处理连续值相关代码,完整代码请点击百度网盘链接下载
创建数据集
def createDataSet():dataSet = [['班长',4.312,'党员','沿海','是'], ['其他',4.238,'党员','贫困县','是'], ['班长',3.373,'党员','山区','是'], ['班长',3.798,'党员','贫困县','是'], ['团支书',4.413,'党员','沿海','是'], ['团支书',4.009,'党员','山区','是'], ['班长',3.893,'党员','贫困县','是'], ['团支书',3.318,'党员','山区','否'], ['团支书',3.184,'团员','山区','否'], ['团支书',2.801,'党员','沿海','否'], ['其他',3.425,'党员','山区','否'], ['其他',3.169,'团员','贫困县','否'], ['其他',4.423,'党员','沿海','否'], ['班长',4.151,'团员','沿海','否'], ['班长',3.462,'党员','沿海','否'], ['班长',3.190,'团员','山区','否'], ['团支书',2.998,'党员','山区','否']]labels = ['担任班委情况','绩点','政治面貌','生源地',]# 特征对应的所有可能的情况labels_full = {}for i in range(len(labels)):labelList = [example[i] for example in dataSet]uniqueLabel = set(labelList)labels_full[labels[i]] = uniqueLabelreturn dataSet, labels, labels_full
按照给定数值,将数据集划分为不大于和大于两部分
def splitDataSetForSeries(dataSet, axis, value):# 用来保存不大于划分值的集合eltDataSet = []# 用来保存大于划分值的集合gtDataSet = []# 进行划分,保留该特征值for feat in dataSet:if (feat[axis] <= value):eltDataSet.append(feat)else:gtDataSet.append(feat)return eltDataSet, gtDataSet
计算连续值的信息增益
def calcInfoGainForSeries(dataSet, i, baseEntropy):
#baseEntropy基础信息熵# 记录最大的信息增益maxInfoGain = 0.0# 最好的划分点bestMid = -1# 得到数据集中所有的当前特征值列表featList = [example[i] for example in dataSet]# 得到分类列表classList = [example[-1] for example in dataSet]dictList = dict(zip(featList, classList))# 将其从小到大排序,按照连续值的大小排列sortedFeatList = sorted(dictList.items(), key=operator.itemgetter(0))print(sortedFeatList)# 计算连续值有多少个numberForFeatList = len(dataSet)# 计算划分点,保留三位小数midFeatList = [round((sortedFeatList[i][0] + sortedFeatList[i+1][0])/2.0, 3)for i in range(numberForFeatList - 1)]# 计算出各个划分点信息增益for mid in midFeatList:# 将连续值划分为不大于当前划分点和大于当前划分点两部分eltDataSet, gtDataSet = splitDataSetForSeries(dataSet, i, mid)# 计算两部分的特征值熵和权重的乘积之和newEntropy = len(eltDataSet)/len(sortedFeatList)*calShannonEnt(eltDataSet) + len(gtDataSet)/len(sortedFeatList)*calShannonEnt(gtDataSet)# 计算出信息增益infoGain = baseEntropy - newEntropyprint('当前划分值为:' + str(mid) + ',此时的信息增益为:' + str(infoGain))if infoGain > maxInfoGain:bestMid = midmaxInfoGain = infoGainreturn maxInfoGain, bestMid
选择最好的数据集划分特征
def chooseBestFeatureToSplit(dataSet, labels):# 得到数据的特征值总数numFeatures = len(dataSet[0]) - 1# 计算出基础信息熵baseEntropy = calShannonEnt(dataSet)# 基础信息增益为0.0bestInfoGain = 0.0# 最好的特征值bestFeature = -1# 标记当前最好的特征值是不是连续值flagSeries = 0# 如果是连续值的话,用来记录连续值的划分点bestSeriesMid = 0.0# 对每个特征值进行求信息熵for i in range(numFeatures):# 得到数据集中所有的当前特征值列表featList = [example[i] for example in dataSet]if isinstance(featList[0], str):infoGain = calcInfoGain(dataSet, featList, i, baseEntropy)else:print('当前划分属性为:' + str(labels[i]))infoGain, bestMid = calcInfoGainForSeries(dataSet, i, baseEntropy)#print('当前特征值为:' + labels[i] + ',对应的信息增益值为:' + str(infoGain))# 如果当前的信息增益比原来的大if infoGain > bestInfoGain:# 最好的信息增益bestInfoGain = infoGain# 新的最好的用来划分的特征值bestFeature = iflagSeries = 0if not isinstance(dataSet[0][bestFeature], str):flagSeries = 1bestSeriesMid = bestMid#print('信息增益最大的特征为:' + labels[bestFeature])if flagSeries:return bestFeature, bestSeriesMidelse:return bestFeature
创建决策树
def createTree(dataSet, labels):# 拿到所有数据集的分类标签classList = [example[-1] for example in dataSet]# 统计第一个标签出现的次数,与总标签个数比较,如果相等则说明当前列表中全部都是一种标签,此时停止划分if classList.count(classList[0]) == len(classList):return classList[0]# 计算第一行有多少个数据,如果只有一个的话说明所有的特征属性都遍历完了,剩下的一个就是类别标签if len(dataSet[0]) == 1:# 返回剩下标签中出现次数较多的那个return majorityCnt(classList)# 选择最好的划分特征,得到该特征的下标bestFeat = chooseBestFeatureToSplit(dataSet, labels)# 得到最好特征的名称bestFeatLabel = ''# 记录此刻是连续值还是离散值,1连续,2离散flagSeries = 0# 如果是连续值,记录连续值的划分点midSeries = 0.0# 如果是元组的话,说明此时是连续值if isinstance(bestFeat, tuple):# 重新修改分叉点信息bestFeatLabel = str(labels[bestFeat[0]]) + '小于' + str(bestFeat[1]) + '?'# 得到当前的划分点midSeries = bestFeat[1]# 得到下标值bestFeat = bestFeat[0]# 连续值标志flagSeries = 1else:# 得到分叉点信息bestFeatLabel = labels[bestFeat]# 离散值标志flagSeries = 0# 使用一个字典来存储树结构,分叉处为划分的特征名称myTree = {bestFeatLabel: {}}# 得到当前特征标签的所有可能值featValues = [example[bestFeat] for example in dataSet]# 连续值处理if flagSeries:# 将连续值划分为不大于当前划分点和大于当前划分点两部分eltDataSet, gtDataSet = splitDataSetForSeries(dataSet, bestFeat, midSeries)# 得到剩下的特征标签subLabels = labels[:]# 递归处理小于划分点的子树subTree = createTree(eltDataSet, subLabels)myTree[bestFeatLabel]['小于'] = subTree# 递归处理大于当前划分点的子树subTree = createTree(gtDataSet, subLabels)myTree[bestFeatLabel]['大于'] = subTreereturn myTree# 离散值处理else:# 将本次划分的特征值从列表中删除掉del (labels[bestFeat])# 唯一化,去掉重复的特征值uniqueVals = set(featValues)# 遍历所有的特征值for value in uniqueVals:# 得到剩下的特征标签subLabels = labels[:]# 递归调用,将数据集中该特征等于当前特征值的所有数据划分到当前节点下,递归调用时需要先将当前的特征去除掉subTree = createTree(splitDataset(dataSet, bestFeat, value), subLabels)# 将子树归到分叉处下myTree[bestFeatLabel][value] = subTreereturn myTree
运行结果
完整代码
链接: https://pan.baidu.com/s/1npNcg7EfQf49SwoGmXriog?pwd=f98b 提取码: f98b
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
