目录
一、数据集的含义
根据是否是985毕业,学历,掌握的技能来评判求职者是否可以录用(这些都是一些离散的点)
二、决策树建立的过程
1、将所有的指标加入到列表中
2、计算其信息熵,保存其最小的信息熵
3、根据最小的信息熵进行划分,若能成功划分,则输出结果,若不能成功划分,则将该指标从列表中溢出后返回步骤2,并将结果添加至决策树中
三、代码解析
1、X,y的array数组
X=np.array([['Yes985','本科','C++'],
['Yes985','本科','Java'],
['No985' ,'硕士','Java'],
['No985' ,'硕士','C++'],
['Yes985','本科','Java'],
['No985' ,'硕士','C++'],
['Yes985','硕士','Java'],
['Yes985','博士','C++'],
['No985' ,'博士','Java'],
['No985' ,'本科','Java']])
y=np.array(['No','Yes','Yes','No','Yes','No','Yes','Yes','Yes','No'])
2、引入的函数包
import numpy as np
from collections import Counter
from math import log2
3、计算信息熵
#计算信息熵
def entropy(y_label):
counter = Counter(y_label)
ent = 0.0
for num in counter.values():
p = num / len(y_label)
ent += -p * log2(p)
return ent
(1)、计算信息熵的公式:H(x) = -∑P(xi)log(2,P(xi)) (i=1,2,..n)。
(2)、counter=Counter(y_label)含义:读取标签数据,并用counter生成标签的字典数组,标签为键,值为出现的次数
4、决策树类
class DecisionTree:
def __init__(self):
self.tree = {}
决策树为字典类型
5、训练决策树
def fit(self,X,y):
cols = list(range(X.shape[1]))
self.tree = self._genTree(cols,X,y)
1)、函数目的为:对X得每一列数据进行分割,用以计算分割后得到的信息熵
2)、shape可输出矩阵的维度,即行数和列数。shape[0]和shape[1]分别代表行和列的长度
3)、X.shape[1]是X中的列数,在本实验中共3列,故X.shape[1]的值为3
4)、range(X.shape[1])表示从0取到3,步长为默认步长:1,输出结果为[0,1,2]
6、递归生成决策树
def _genTree(self,cols,X,y):
imin=cols[0]#最小熵的列
emin=100#最小熵值,此时imin是一个int型函数,值为0
for i in cols:
coli = X[:,i]#拿到第i个特征数据
enti = sum([entropy(y[coli==d]) for d in set(coli)])
if enti < emin:
imin = i
emin = enti
newtree={}
mincol = X[:,imin]
cols.remove(imin)
for d in set(mincol):
entd = entropy(y[mincol==d])
if entd <1e-10:
newtree[d] = y[mincol==d][0]
else:
newtree[d] = self._genTree(cols.copy(),X[mincol==d,:],y[mincol==d])
return { imin : newtree}#将列号作为索引,返回新生成的树
1)、 函数目的为:计算最小信息熵的特征
2)、coli = X[:,i]是为了拿到第i个特征数据 。X[:,i]是提取X中每一个行中的第i个元素
3)、enti = sum([entropy(y[coli==d]) for d in set(coli)])
for d in set(coli)含义:将信息熵的转换为集合的形式,如:当coli是‘Yes985’,‘No985’这一个特征时,其输出结果只有两个值,‘Yes985’和‘No985’。特别注意:set(coli)是将coli里面重复的函数去掉,同时将其转化为集合的形式。
4)、若单看y[coli==d] for d in set(coli)时,其目的是输出y中所有满足coli==d的元素,故sum([entropy(y[coli==d]) for d in set(coli)])的含义是,将y[coli==d]作为一个标签代入到entropy()函数中,进行运算,并求和
5)、newtree={}的含义是为:根据最小熵特征有几个值,就生成几个新的子树分支 newtree={}
6)、mincol = X[:,imin]是获得最小熵特征的那一个列,当输出mincol得到
说明第一次得到最小熵的列是技能这一列,将技能这一列移除后,获取技能中‘JAVA’这一分支的最小熵,即是否是985,依次获取每一分支上的最小熵,直至已经完全分开(可见最下方的图示)
7)、cols.remove(imin)是将搜索到的熵值最小的imin这一列列移除 ,再针对这个特征得每个值,进一步划分树
8)、y[mincol==d][0]:该语句是在if条件中,因为已经完全分开了,所以最后得出的结果一致,均为yes或均为no,故需要取出第一个元素
9)、newtree[d] = self._genTree(cols.copy(),X[mincol==d,:],y[mincol==d])
其中cols.copy()为复制去除最小信息熵这个特征值后的cols,然后参与下一次的递归运算,
X[mincol==d,:]是指取出所有满足mincol==d这一条件的求职者信息,直至该树的叶子结点为Yes或No为止
y[mincol==d]是指保存X[mincol==d,:]中所对应的y类别标签
10)、在改代码中,如果entd的值小于1e-10,则判断结束,否则则进行递归运算,若始终无法使entd的值小于1e-10,则当cols里面的特征值没有的时候,则不进行循环,直接输出结果
7、预测结果
def predict(self, X):
X=X.tolist()
y=[None for i in range(len(X))]
for i in range (len(X)):
Predict_tree=self.tree
while Predict_tree!='Yes' and Predict_tree!='No':
cols=list(Predict_tree.keys())[0]
Predict_tree=Predict_tree[cols]
Predict_tree=Predict_tree[X[i][cols]]
y[i]=Predict_tree
return y
1)、X=X.tolist()将X转换为列表的形式,此时X是一个二维列表
2)、y=[None for i in range(len(X))]生成一个长度为len(X)的空列表
3)、进入循环,当Predict_tree为Yes或No时结束循环
4)、cols=list(Predict_tree.keys())[0]是指将最外层字典的键取出,转换为列表,取第一个元素
5)、Predict_tree=Predict_tree[cols],指用cols值所对应的分支进行向下延伸
6)、Predict_tree=Predict_tree[X[i][cols]],根据X中第i个求职者的第cols个信息进行判断
8、输出结果
dt = DecisionTree()
dt.fit(X,y)
print(dt.tree)
print(dt.predict(X))
四、辅助图示
由图可知,只有当叶子结点为Yes或No的时候,判断才结束
在本文中,采用字典的形式来表示一颗树