Python3 ID3决策树判断申请贷款是否成功的实现代码


Posted in Python onMay 21, 2020

1. 定义生成树

# -*- coding: utf-8 -*-
#生成树的函数

from numpy import * 
import numpy as np
import pandas as pd
from math import log 
import operator 

# 计算数据集的信息熵(Information Gain)增益函数(机器学习实战中信息熵叫香农熵)
def calcInfoEnt(dataSet):#本题中Label即好or坏瓜 #dataSet每一列是一个属性(列末是Label)
 numEntries = len(dataSet) #每一行是一个样本
 labelCounts = {} #给所有可能的分类创建字典labelCounts
 for featVec in dataSet: #按行循环:即rowVev取遍了数据集中的每一行
  currentLabel = featVec[-1] #故featVec[-1]取遍每行最后一个值即Label
  if currentLabel not in labelCounts.keys(): #如果当前的Label在字典中还没有
   labelCounts[currentLabel] = 0 #则先赋值0来创建这个词
  labelCounts[currentLabel] += 1 #计数, 统计每类Label数量(这行不受if限制)
 InfoEnt = 0.0
 for key in labelCounts: #遍历每类Label
  prob = float(labelCounts[key])/numEntries #各类Label熵累加
  InfoEnt -= prob * log(prob,2) #ID3用的信息熵增益公式
 return InfoEnt

### 对于离散特征: 取出该特征取值为value的所有样本
def splitDiscreteDataSet(dataSet, axis, value): #dataSet是当前结点(待划分)集合,axis指示划分所依据的属性,value该属性用于划分的取值
 retDataSet = []  #为return Data Set分配一个列表用来储存
 for featVec in dataSet:
  if featVec[axis] == value:
   reducedFeatVec = featVec[:axis]   #该特征之前的特征仍保留在样本dataSet中
   reducedFeatVec.extend(featVec[axis+1:]) #该特征之后的特征仍保留在样本dataSet中
   retDataSet.append(reducedFeatVec)  #把这个样本加到list中
 return retDataSet

### 对于连续特征: 返回特征取值大于value的所有样本(以value为阈值将集合分成两部分)
def splitContinuousDataSet(dataSet, axis, value): 
 retDataSetG = []  #将储存取值大于value的样本
 retDataSetL = []  #将储存取值小于value的样本 
 for featVec in dataSet: 
  if featVec[axis] > value: 
   reducedFeatVecG = featVec[:axis]
   reducedFeatVecG.extend(featVec[axis+1:]) 
   retDataSetG.append(reducedFeatVecG)
  else:
   reducedFeatVecL = featVec[:axis]
   reducedFeatVecL.extend(featVec[axis+1:]) 
   retDataSetL.append(reducedFeatVecL)
 return retDataSetG,retDataSetL  #返回两个集合, 是含2个元素的tuple形式

### 根据InfoGain选择当前最好的划分特征(以及对于连续变量还要选择以什么值划分)
def chooseBestFeatureToSplit(dataSet,labels): 
 numFeatures = len(dataSet[0])-1
 baseEntropy = calcInfoEnt(dataSet) 
 bestInfoGain = 0.0; bestFeature = -1
 bestSplitDict = {}
 for i in range(numFeatures):
  #遍历所有特征:下面这句是取每一行的第i个, 即得当前集合所有样本第i个feature的值
  featList = [example[i] for example in dataSet]
  #判断是否为离散特征
  if not (type(featList[0]).__name__=='float' or type(featList[0]).__name__=='int'): 
# 对于离散特征:求若以该特征划分的熵增
   uniqueVals = set(featList)  #从列表中创建集合set(得列表唯一元素值)
   newEntropy = 0.0
   for value in uniqueVals:  #遍历该离散特征每个取值
    subDataSet = splitDiscreteDataSet(dataSet, i, value)#计算每个取值的信息熵
    prob = len(subDataSet)/float(len(dataSet))
    newEntropy += prob * calcInfoEnt(subDataSet)#各取值的熵累加
   infoGain = baseEntropy - newEntropy #得到以该特征划分的熵增 
# 对于连续特征:求若以该特征划分的熵增(区别:n个数据则需添n-1个候选划分点, 并选最佳划分点) 
  else: #产生n-1个候选划分点 
   sortfeatList=sorted(featList) 
   splitList=[] 
   for j in range(len(sortfeatList)-1): #产生n-1个候选划分点
    splitList.append((sortfeatList[j] + sortfeatList[j+1])/2.0) 
   bestSplitEntropy = 10000     #设定一个很大的熵值(之后用)
   #遍历n-1个候选划分点: 求选第j个候选划分点划分时的熵增, 并选出最佳划分点
   for j in range(len(splitList)):
    value = splitList[j] 
    newEntropy = 0.0 
    DataSet = splitContinuousDataSet(dataSet, i, value)
    subDataSetG = DataSet[0]
    subDataSetL = DataSet[1] 
    probG = len(subDataSetG) / float(len(dataSet)) 
    newEntropy += probG * calcInfoEnt(subDataSetG) 
    probL = len(subDataSetL) / float(len(dataSet)) 
    newEntropy += probL * calcInfoEnt(subDataSetL)
    if newEntropy < bestSplitEntropy: 
     bestSplitEntropy = newEntropy
     bestSplit = j
   bestSplitDict[labels[i]] = splitList[bestSplit]#字典记录当前连续属性的最佳划分点
   infoGain = baseEntropy - bestSplitEntropy  #计算以该节点划分的熵增
# 在所有属性(包括连续和离散)中选择可以获得最大熵增的属性
  if infoGain > bestInfoGain: 
   bestInfoGain = infoGain
   bestFeature = i
 #若当前节点的最佳划分特征为连续特征,则需根据“是否小于等于其最佳划分点”进行二值化处理
 #即将该特征改为“是否小于等于bestSplitValue”, 例如将“密度”变为“密度<=0.3815”
 #注意:以下这段直接操作了原dataSet数据, 之前的那些float型的值相应变为0和1
 #【为何这样做?】在函数createTree()末尾将看到解释
 if type(dataSet[0][bestFeature]).__name__=='float' or type(dataSet[0][bestFeature]).__name__=='int':  
  bestSplitValue = bestSplitDict[labels[bestFeature]] 
  labels[bestFeature] = labels[bestFeature] + '<=' + str(bestSplitValue)
  for i in range(shape(dataSet)[0]): 
   if dataSet[i][bestFeature] <= bestSplitValue: 
    dataSet[i][bestFeature] = 1 
   else: 
    dataSet[i][bestFeature] = 0
 return bestFeature  

# 若特征已经划分完,节点下的样本还没有统一取值,则需要进行投票:计算每类Label个数, 取max者
def majorityCnt(classList): 
 classCount = {}  #将创建键值为Label类型的字典
 for vote in classList: 
  if vote not in classCount.keys(): 
   classCount[vote] = 0  #第一次出现的Label加入字典
  classCount[vote] += 1  #计数
 return max(classCount)

2. 递归产生决策树

# 主程序:递归产生决策树
 # dataSet:当前用于构建树的数据集, 最开始就是data_full,然后随着划分的进行越来越小。这是因为进行到到树分叉点上了. 第一次划分之前17个瓜的数据在根节点,然后选择第一个bestFeat是纹理. 纹理的取值有清晰、模糊、稍糊三种;将瓜分成了清晰(9个),稍糊(5个),模糊(3个),这时应该将划分的类别减少1以便于下次划分。 
 # labels:当前数据集中有的用于划分的类别(这是因为有些Label当前数据集没了, 比如假如到某个点上西瓜都是浅白没有深绿了)
 # data_full:全部的数据 
 # label_full:全部的类别 

numLine = numColumn = 2 #这句是因为之后要用global numLine……至于为什么我一定要用global

# 我也不完全理解。如果我只定义local变量总报错,我只好在那里的if里用global变量了。求解。

def createTree(dataSet,labels,data_full,labels_full): 
 classList = [example[-1] for example in dataSet] 
 #递归停止条件1:当前节点所有样本属于同一类;(注:count()方法统计某元素在列表中出现的次数)
 if classList.count(classList[0]) == len(classList): 
  return classList[0]
 
#递归停止条件2:当前节点上样本集合为空集(即特征的某个取值上已经没有样本了):
 global numLine,numColumn
 (numLine,numColumn) = shape(dataSet)
 if float(numLine) == 0: 
  return 'empty'
 
#递归停止条件3:所有可用于划分的特征均使用过了,则调用majorityCnt()投票定Label;
 if float(numColumn) == 1: 
  return majorityCnt(classList) 
 
#不停止时继续划分:
 bestFeat = chooseBestFeatureToSplit(dataSet,labels)#调用函数找出当前最佳划分特征是第几个
 bestFeatLabel = labels[bestFeat]  #当前最佳划分特征
 myTree = {bestFeatLabel:{}} 
 featValues = [example[bestFeat] for example in dataSet] 
 uniqueVals = set(featValues) 
 if type(dataSet[0][bestFeat]).__name__=='str': 
  currentlabel = labels_full.index(labels[bestFeat]) 
  featValuesFull = [example[currentlabel] for example in data_full] 
  uniqueValsFull = set(featValuesFull) 
 del(labels[bestFeat]) #划分完后, 即当前特征已经使用过了, 故将其从“待划分特征集”中删去

 #【递归调用】针对当前用于划分的特征(beatFeat)的每个取值,划分出一个子树。 
 for value in uniqueVals: #遍历该特征【现存的】取值
  subLabels = labels[:] 
  if type(dataSet[0][bestFeat]).__name__=='str': 
   uniqueValsFull.remove(value)  #划分后删去(从uniqueValsFull中删!)
  myTree[bestFeatLabel][value] = createTree(splitDiscreteDataSet(dataSet,bestFeat,value),subLabels,data_full,labels_full)#用splitDiscreteDataSet()
 #是由于, 所有的连续特征在划分后都被我们定义的chooseBestFeatureToSplit()处理成离散取值了。
 if type(dataSet[0][bestFeat]).__name__=='str': #若该特征离散【更详见后注】
  for value in uniqueValsFull:#则可能有些取值已经不在【现存的】取值中了
 #这就是上面为何从“uniqueValsFull”中删去
 #因为那些现有数据集中没取到的该特征的值,保留在了其中
   myTree[bestFeatLabel][value] = majorityCnt(classList) 

 return myTree

3. 调用生成树

#生成树调用的语句
df = pd.read_excel(r'E:\BaiduNetdiskDownload\spss\数据\实验data\银行贷款.xlsx') 
data = df.values[:,1:].tolist() 
data_full = data[:] 
labels = df.columns.values[1:-1].tolist() 
labels_full = labels[:] 
myTree = createTree(data,labels,data_full,labels_full)

查看数据

data

Python3 ID3决策树判断申请贷款是否成功的实现代码

labels

Python3 ID3决策树判断申请贷款是否成功的实现代码

4. 绘制决策树

#绘决策树的函数
import matplotlib.pyplot as plt 
decisionNode = dict(boxstyle = "sawtooth",fc = "0.8") #定义分支点的样式
leafNode = dict(boxstyle = "round4",fc = "0.8") #定义叶节点的样式
arrow_args = dict(arrowstyle = "<-") #定义箭头标识样式

# 计算树的叶子节点数量 
def getNumLeafs(myTree):
 numLeafs = 0 
 firstStr = list(myTree.keys())[0]
 secondDict = myTree[firstStr]
 for key in secondDict.keys(): 
  if type(secondDict[key]).__name__=='dict': 
   numLeafs += getNumLeafs(secondDict[key]) 
  else: numLeafs += 1
 return numLeafs

# 计算树的最大深度
def getTreeDepth(myTree): 
 maxDepth = 0 
 firstStr = list(myTree.keys())[0] 
 secondDict = myTree[firstStr] 
 for key in secondDict.keys(): 
  if type(secondDict[key]).__name__=='dict': 
   thisDepth = 1 + getTreeDepth(secondDict[key]) 
  else: thisDepth = 1 
  if thisDepth > maxDepth: 
   maxDepth = thisDepth
 return maxDepth 

# 画出节点 
def plotNode(nodeTxt,centerPt,parentPt,nodeType): 
 createPlot.ax1.annotate(nodeTxt,xy = parentPt,xycoords = 'axes fraction',xytext = centerPt,textcoords = 'axes fraction',va = "center", ha = "center",bbox = nodeType,arrowprops = arrow_args) 

# 标箭头上的文字 
def plotMidText(cntrPt,parentPt,txtString): 
 lens = len(txtString) 
 xMid = (parentPt[0] + cntrPt[0]) / 2.0 - lens*0.002 
 yMid = (parentPt[1] + cntrPt[1]) / 2.0 
 createPlot.ax1.text(xMid,yMid,txtString) 

def plotTree(myTree,parentPt,nodeTxt): 
 numLeafs = getNumLeafs(myTree) 
 depth = getTreeDepth(myTree) 
 firstStr = list(myTree.keys())[0] 
 cntrPt = (plotTree.x0ff + (1.0 + float(numLeafs))/2.0/plotTree.totalW,plotTree.y0ff) 
 plotMidText(cntrPt,parentPt,nodeTxt) 
 plotNode(firstStr,cntrPt,parentPt,decisionNode) 
 secondDict = myTree[firstStr] 
 plotTree.y0ff = plotTree.y0ff - 1.0/plotTree.totalD 
 for key in secondDict.keys(): 
  if type(secondDict[key]).__name__=='dict': 
   plotTree(secondDict[key],cntrPt,str(key)) 
  else: 
   plotTree.x0ff = plotTree.x0ff + 1.0/plotTree.totalW 
   plotNode(secondDict[key],(plotTree.x0ff,plotTree.y0ff),cntrPt,leafNode) 
   plotMidText((plotTree.x0ff,plotTree.y0ff),cntrPt,str(key)) 
 plotTree.y0ff = plotTree.y0ff + 1.0/plotTree.totalD 

def createPlot(inTree): 
 fig = plt.figure(1,facecolor = 'white') 
 fig.clf() 
 axprops = dict(xticks = [],yticks = []) 
 createPlot.ax1 = plt.subplot(111,frameon = False,**axprops) 
 plotTree.totalW = float(getNumLeafs(inTree)) 
 plotTree.totalD = float(getTreeDepth(inTree)) 
 plotTree.x0ff = -0.5/plotTree.totalW 
 plotTree.y0ff = 1.0 
 plotTree(inTree,(0.5,1.0),'') 
 plt.show()

5. 调用函数

#命令绘决策树的图
createPlot(myTree)

myTree

总结

到此这篇关于Python3 ID3决策树判断申请贷款是否成功的实现代码的文章就介绍到这了,更多相关python ID3 决策树判断内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
python 数据加密代码
Dec 24 Python
操作Windows注册表的简单的Python程序制作教程
Apr 07 Python
用python结合jieba和wordcloud实现词云效果
Sep 05 Python
python通过Windows下远程控制Linux系统
Jun 20 Python
python实现列表中由数值查到索引的方法
Jun 27 Python
python程序变成软件的实操方法
Jun 24 Python
使用 python pyautogui实现鼠标键盘控制功能
Aug 04 Python
基于keras 模型、结构、权重保存的实现
Jan 24 Python
python实现opencv+scoket网络实时图传
Mar 20 Python
python3.6.5基于kerberos认证的hive和hdfs连接调用方式
Jun 06 Python
Visual Studio code 配置Python开发环境
Sep 11 Python
Python数据可视化之Seaborn的安装及使用
Apr 19 Python
Python使用os.listdir和os.walk获取文件路径
May 21 #Python
keras 权重保存和权重载入方式
May 21 #Python
浅谈keras保存模型中的save()和save_weights()区别
May 21 #Python
Python通过文本和图片生成词云图
May 21 #Python
解决在keras中使用model.save()函数保存模型失败的问题
May 21 #Python
Python 实现敏感目录扫描的示例代码
May 21 #Python
基于python检查矩阵计算结果
May 21 #Python
You might like
用PHP和ACCESS写聊天室(十)
2006/10/09 PHP
初识PHP
2014/09/28 PHP
php实现插入数组但不影响原有顺序的方法
2015/03/27 PHP
php检查页面是否被百度收录
2015/10/28 PHP
Zend Framework教程之Zend_Db_Table表关联实例详解
2016/03/23 PHP
yii2实现分页,带搜索的分页功能示例
2017/01/07 PHP
PHP机器学习库php-ml的简单测试和使用方法
2017/07/14 PHP
PHP SFTP实现上传下载功能
2017/07/26 PHP
JS trim去空格的最佳实践
2011/10/30 Javascript
微信小程序 WebSocket详解及应用
2017/01/21 Javascript
JS实现电商放大镜效果
2017/08/24 Javascript
Angular2.0/4.0 使用Echarts图表的示例代码
2017/12/07 Javascript
Vue2 模板template的四种写法总结
2018/02/23 Javascript
Webpack中雪碧图插件使用详解
2018/05/25 Javascript
Javascript作用域和作用域链原理解析
2020/03/03 Javascript
在VUE style中使用data中的变量的方法
2020/06/19 Javascript
[02:32]DOTA2亚洲邀请赛 C9战队出场宣传片
2015/02/07 DOTA
[01:21]2018DOTA2亚洲邀请赛4.5采访 打DOTA2也能有女朋友?
2018/04/06 DOTA
python对指定目录下文件进行批量重命名的方法
2015/04/18 Python
python实现中文输出的两种方法
2015/05/09 Python
python操作 hbase 数据的方法
2016/12/18 Python
django 自定义过滤器的实现
2019/02/26 Python
python构建指数平滑预测模型示例
2019/11/21 Python
详解python环境安装selenium和手动下载安装selenium的方法
2020/03/17 Python
基于python实现计算两组数据P值
2020/07/10 Python
python 如何利用argparse解析命令行参数
2020/09/11 Python
Python中Selenium模块的使用详解
2020/10/09 Python
玩转CSS3色彩
2010/01/16 HTML / CSS
使用HTML5的File实现base64和图片的互转
2013/08/01 HTML / CSS
HTML 5.1来了 9月份正式发布 更新内容预览
2016/04/26 HTML / CSS
《草原的早晨》教学反思
2014/04/08 职场文书
道歉短信大全
2015/05/12 职场文书
创业计划书之青年旅馆
2019/09/23 职场文书
导游词之四川武侯祠
2019/10/21 职场文书
Nginx解决403 forbidden的完整步骤
2021/04/01 Servers
JavaScript前端面试扁平数据转tree与tree数据扁平化
2022/06/14 Javascript