#!/user/bin/env python
# !-*-coding:utf-8 -*-
# !Time :2018/9/28 4:12 PM
# !Author : hyCong
# !@File : .py
from math import log
import operator
import treePlotter
# 计算熵
def calcShannonEnt(dataSet):
numEntries = len(dataSet) # 数据集的长度
labelCounts = {} # 用来存放不同这一特征的不同类型的样例个数
for featVec in dataSet:
currentLabel = featVec[-1] # 获取当前样例的类别
if currentLabel not in labelCounts.keys(): # 判断当前扫到的类别在labelCounts字典中是否存在,如果存在则出现次数加以,否则初始化为1
labelCounts[currentLabel] = 1
else:
labelCounts[currentLabel] += 1
shannonEnt = 0.0 # 初始化香浓⤴️为0
for key in labelCounts:
prob = float(labelCounts[key]) / numEntries # 计算当前key类别的样例个数/总个数,即Pk
shannonEnt -= prob * log(prob, 2) # 使用熵的计算公式计算熵
return shannonEnt
# 判断简单鱼类的数据创建函数
def createDataSet():
dataSet = [
[1, 1, 'yes'],
[1, 1, 'yes'],
[1, 0, 'no'],
[0, 1, 'no'],
[0, 1, 'no']
]
labels = ['no surfacing', 'flippers']
return dataSet, labels
# 根据所给定的属性和属性值获取相应的数据
def splitDataSet(dataSet, axis, value): # 三个参数,数据集,所选定的属性下标,需要返回的数据集的属性的值
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value: # 如果符合要求,则将该行数据抽取出来(去除用于选择的属性列)
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis + 1:])
retDataSet.append(reducedFeatVec)
return retDataSet
# ID3(迭代二分器)中的划分属性选择方法:从候选的属性中选出Gain最大的属性进行划分
def chooseBestFeatureToSplitInID3(dataSet):
numFeatures = len(dataSet[0]) - 1 # 待选属性的个数
baseEntropy = calcShannonEnt(dataSet) # Ent(D) 数据集的信息熵
bestInfoGain = 0.0;
bestFeatur = -1 # 初始化最大信息增益和相对应的属性下标
for i in range(numFeatures):
featList = [example[i] for example in dataSet] # 获取第i个属性的对应列
uniqueVals = set(featList) # 获取属性值集合
newEntropy = 0.0 # 属性信息熵
for value in uniqueVals: # 针对第i种属性的每一种属性值,进行循环,计算其对应的信息熵
subDataSet = splitDataSet(dataSet, i, value) # 抽取出i属性的value属性值得数据集
prob = len(subDataSet) / float(len(dataSet)) # 计算Pk(v)
newEntropy += prob * calcShannonEnt(subDataSet) # Gain
infoGain = baseEntropy - newEntropy # i属性的信息增益
if infoGain > bestInfoGain: # 取出信息增益最大的属性
bestInfoGain = infoGain
bestFeatur = i
return bestFeatur
# C4.5算法中的划分属性选择方法:使用启发式,先从候选划分属性中找出信息增益高于平均水平的那些属性,再从中选出Gain_ratio信息增益率最大的属性
def chooseBestFeatureToSplitInC45(dataSet):
numFeatures = len(dataSet[0]) - 1 # 待选属性的个数
baseEntropy = calcShannonEnt(dataSet) # Ent(D) 数据集的信息熵
bestGainRatio = 0.0 # 最高信息增益率
gain_ratios = [] # 各属性的信息增益率
gain_ratios_high = [] # 比平均信息增益大的属性的信息增益率
feats_high = [] # 比平均信息增益大的属性下标
sumEntropy = 0.0 # 总信息增益
gainSet = [] # 各属性的信息增益
bestFeatur = -1 # 初始化最大信息增益和相对应的属性下标
for i in range(numFeatures):
featList = [example[i] for example in dataSet] # 获取第i个属性的对应列
uniqueVals = set(featList) # 获取属性值集合
newEntropy = 0.0 # 属性信息熵
IV = 0.0
for value in uniqueVals: # 针对第i种属性的每一种属性值,进行循环,计算其对应的信息熵
subDataSet = splitDataSet(dataSet, i, value) # 抽取出i属性的value属性值得数据集
prob = len(subDataSet) / float(len(dataSet)) # 计算Pk(v)
newEntropy += prob * calcShannonEnt(subDataSet) # Gain
IV -= prob * log(prob, 2) # 属性i的固有值
infoGain = baseEntropy - newEntropy # i属性的信息增益
gain_ratios.append(infoGain / IV) # 记录下属性i的信息增益率
sumEntropy += infoGain # 计算总信息增益
gainSet.append(infoGain) # 将各属性的信息增益放入列表用于计算平均值
avgEntropy = sumEntropy / numFeatures # 信息增益平均值
for i in range(len(gainSet)): # 循环找出大于平均信息增益的属性
if gainSet[i] > avgEntropy:
gain_ratios_high.append(gain_ratios[i])
feats_high.append(i)
for i in range(len(gain_ratios_high)): # 找出增益率最大的属性
if gain_ratios_high[i] > bestGainRatio:
bestGainRatio = gain_ratios_high[i]
bestFeatur = feats_high[i]
return bestFeatur
# CART决策树算法中划分属性的选择:选择属性的基尼系数最小的进行划分
def chooseBestFeatureToSplitInCART(dataSet):
return 0
# 当候选属性已经划分完毕后,发现数据集仍然存在多个类别,则采用多数表决的方法进行决定分类
def majorityCnt(classList):
classCount = {} # 类别计数
for vote in classList:
if vote not in classCount.keys():
classCount[vote] = 1
else:
classCount[vote] += 1
sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True) # 将统计好的各类别的样本个数进行降序排序
return sortedClassCount[0][0] # 返回出现次数最多的类别
# 决策树创建代码
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 = chooseBestFeatureToSplitInID3(dataSet) # 选取剩下的属性中最优的属性
bestFeatLabel = labels[bestFeat] # 最优属性的名称
myTree = {bestFeatLabel: {}} # 新建用来存放树节点的字典
del (labels[bestFeat]) # 删除当前已选择了的属性名称
featValues = [example[bestFeat] for example in dataSet] # 循环将数据集中bestFeature的所有属性值取出
uniqueVals = set(featValues) # 将获取的属性值进行去重
for vallue in uniqueVals:
subLabels = labels[:] # python在函数调用时,是引用传值,而labels是列表类型,实质上是一个指针,若直接传入,可能会导致操作错误。
# 进入下一次迭代的入口,在进入子树进行创建时,首先需要将数据集进行划分,抽取出所有符合当前选择的属性的属性值的数据,将其传入下一次创建过程。
myTree[bestFeatLabel][vallue] = createTree(splitDataSet(dataSet, bestFeat, vallue), subLabels)
return myTree
classLabel = ''
# 针对测试向量,判断其类别
def classify(inputTree, featLabels, testVec):
global classLabel
firstStr = inputTree.keys()[0] # 获取当前树的树根的属性名
secondDict = inputTree[firstStr] # 获取根节点的孩子节点列表
featIndex = featLabels.index(firstStr) # 找到当前根节点属性在属性列表中的下标
for key in secondDict.keys(): # 对孩子节点进行扫描
if testVec[featIndex] == key: # 若测试向量中有属性和某一孩子节点相同,则判断该孩子节点是否还有子树,若有则继续向下寻找叶节点,否则则返回孩子节点的分类标签
if type(secondDict[key]).__name__ == 'dict':
classLabel = classify(secondDict[key], featLabels, testVec)
else:
classLabel = secondDict[key]
return classLabel
# 使用pickle模块存储决策树
def storeTree(inputTree, filename):
import pickle
fw = open(filename, 'w')
pickle.dump(inputTree, fw)
fw.close()
def grabTree(filename):
import pickle
fr = open(filename)
return pickle.load(fr)
``
机器学习实战-简单决策树编写
最新推荐文章于 2020-07-27 13:31:50 发布