更高级的实现方法2如下:
'''计算数据集的香农熵(信息期望值):熵越高表示混合数据越多,度量数据集无序程度''' def calcShannonEnt(dataSet): # 需要对 list 中的大量计数时,可以直接使用Counter,不用新建字典来计数 label_count = Counter(data[-1] for data in dataSet) # 统计标签出现的次数 probs = [p[1] / len(dataSet) for p in label_count.items()] # 计算概率 shannonEnt = sum([-p * log(p, 2) for p in probs]) # 计算香农熵 print(Decimal(shannonEnt).quantize(Decimal('0.00000'))) return shannonEnt调用运行如下:
# 2 计算数据集的熵 calcShannonEnt(dataset)按照给定的特征划分数据集
我们根据信息熵度量出来的特征,进行数据集划分方法1如下:
'''划分数据集:按照特征划分''' def splitDataSet(dataSet, index, value): retDataSet = [] for featVec in dataSet: if featVec[index] == value:# 判断index列的值是否为value reducedFeatVec = featVec[:index] # [:index]表示取前index个特征 reducedFeatVec.extend(featVec[index+1:]) # 取接下来的数据 retDataSet.append(reducedFeatVec) print(retDataSet) return retDataSet我们根据信息熵度量出来的特征,进行数据集划分方法2如下:
'''划分数据集:按照特征划分''' def splitDataSet(dataSet, index, value): retDataSet = [data for data in dataSet for i, v in enumerate(data) if i == index and v == value] print(retDataSet) return retDataSet指定特征的数据集划分方法调用
#3 划分数据集 splitDataSet(dataset,0,1)运行结果如下:
[[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no']]选择最好的数据集划分方式
选择最好的数据集划分方式:特征选择,划分数据集、计算最好的划分数据集特征,方法1如下:
''' 注意:一是数据集列表元素具备相同数据长度,二是最后一列是标签列 ''' def chooseBestFeatureToSplit(dataSet): numFeatures = len(dataSet[0]) - 1 # 特征总个数, 最后一列是标签 baseEntropy = calcShannonEnt(dataSet) # 计算数据集的信息熵 bestInfoGain, bestFeature = 0.0, -1 # 最优的信息增益值, 和最优的Featurn编号 for i in range(numFeatures): featList = [example[i] for example in dataSet] # 获取各实例第i+1个特征 uniqueVals = set(featList) # 获取去重后的集合 newEntropy = 0.0 # 创建一个新的信息熵 for value in uniqueVals: subDataSet = splitDataSet(dataSet, i, value) prob = len(subDataSet)/float(len(dataSet)) newEntropy += prob * calcShannonEnt(subDataSet) # 比较所有特征中的信息增益,返回最好特征划分的索引值。 infoGain = baseEntropy - newEntropy print('infoGain=', infoGain, 'bestFeature=', i, baseEntropy, newEntropy) if (infoGain > bestInfoGain): bestInfoGain = infoGain bestFeature = i # print(bestFeature) return bestFeature选择最好的数据集划分方式:特征选择,划分数据集、计算最好的划分数据集特征,方法2如下:
''' 注意:一是数据集列表元素具备相同数据长度,二是最后一列是标签列 ''' def chooseBestFeatureToSplit(dataSet): base_entropy = calcShannonEnt(dataSet) # 计算初始香农熵 best_info_gain = 0 best_feature = -1 # 遍历每一个特征 for i in range(len(dataSet[0]) - 1): # 对当前特征进行统计 feature_count = Counter([data[i] for data in dataSet]) # 计算分割后的香农熵 new_entropy = sum(feature[1] / float(len(dataSet)) * calcShannonEnt(splitDataSet(dataSet, i, feature[0])) for feature in feature_count.items()) # 更新值 info_gain = base_entropy - new_entropy # print('No. {0} feature info gain is {1:.3f}'.format(i, info_gain)) if info_gain > best_info_gain: best_info_gain = info_gain best_feature = i # print(best_feature) return best_feature选择最好的数据集划分方法调用
# 4 选择最好的数据集划分方式 chooseBestFeatureToSplit(dataset))运行结果如下:
infoGain= 0.4199730940219749 bestFeature= 0 0.9709505944546686 0.5509775004326937 infoGain= 0.17095059445466854 bestFeature= 1 0.9709505944546686 0.8 选择:0 训练算法:构造树的数据结构创建树的函数代码如下:
'''创建决策树''' def createTree(dataSet, labels): classList = [example[-1] for example in dataSet] # 如果数据集的最后一列的第一个值出现的次数=整个集合的数量,也就说只有一个类别,就只直接返回结果就行 # 第一个停止条件:所有的类标签完全相同,则直接返回该类标签。 # count() 函数是统计括号中的值在list中出现的次数 if classList.count(classList[0]) == len(classList): return classList[0] # 如果数据集只有1列,那么最初出现label次数最多的一类,作为结果 # 第二个停止条件:使用完了所有特征,仍然不能将数据集划分成仅包含唯一类别的分组。 if len(dataSet[0]) == 1: return majorityCnt(classList) # 选择最优的列,得到最优列对应的label含义 bestFeat = chooseBestFeatureToSplit(dataSet) # 获取label的名称 bestFeatLabel = labels[bestFeat] # 初始化myTree myTree = {bestFeatLabel: {}} # 所以这行代码导致函数外的同名变量被删除了元素,造成例句无法执行,提示'no surfacing' is not in list # del(labels[bestFeat]) # 取出最优列,然后它的branch做分类 featValues = [example[bestFeat] for example in dataSet] uniqueVals = set(featValues) for value in uniqueVals: # 求出剩余的标签label subLabels = labels[:] # 遍历当前选择特征包含的所有属性值,在每个数据集划分上递归调用函数createTree() myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels) # print('myTree', value, myTree) print(myTree) return myTree