什么是决策树
决策树是通过树结构进行决策的,非常像人在对某件事进行决策的过程。例如:我们要买房子,首先我们要考虑买“新房”还是“二手房”,如果是”新房“,那接下来看”交通是否便利“,如果便利,我们再判断下一个条件,最后我们决定“买它!”。买房或者不买房,我们可以看成一种而分类问题,那么我们可以通过树的结构去表现出咱们的决策过程。
举个例子:
我要买房子,并且这个房子必须是学区房,那么我们买房子最重要的因素就是这个房子是否靠近学校,那么我再考虑我的存款是否能负担得起首付,假设我们只考虑这两点,那么我们决策过程能可视化成下面这种图:
现在我们有一处房源:
区域 | 售价 |
---|---|
某某某高中旁边 | 100万 |
根据这一处的房源,我们来进行决策,首先我们看区域,高中旁边,那么我们在上面这种图中就满足了区域的要求,我们走向售价方面,这个房子售价100万,我手里有200万,那么我们肯定会进行交易,把这房子拿下来。但是上图的二叉树是我们已经构建好的,而在实际分类的过程中,我们需要根据基本流程把决策树构建出来。
基本流程
- 输入数据集
- 构建根节点,将所有的数据放在根节点。
- 从特征集中选择最优特征。
- 进行判断:如果子集已经能够被基本正确分类,构建叶子结点,到5。
如果还有子集不能被基本分类正确,特征集中去除已选的最有特征,回到3 - 输出决策树
看了上述过程,最懵逼的应该是如果从特征集中选择最优特征,到底我是先看看自己有没有钱买得起房子,还是先看这个房子到底是不是学区房。显然,决策树学习的算法是一个递归地选择最优特征,最重要的就是如何选择最优特征。
最优特征
下面就是看公式,了解公式的过程啦。
信息增益
“熵”也称“信息熵”是表示随机变量不确定性的度量。
设
X
X
X是一个取有限个值得离散随机变量,其概率分布为:
P
(
X
=
X
i
)
=
p
i
,
i
=
1
,
2
,
…
,
n
P(X=X_i) = p_i, i=1,2,\dots,n
P(X=Xi)=pi,i=1,2,…,n
则随机变量
X
X
X的熵表示为:
H
(
x
)
=
−
∑
i
=
1
n
p
i
log
p
i
i
f
p
i
=
0
,
0
log
0
=
0
H(x) = - \sum_{i=1}^np_i\log pi \qquad if \ p_i = 0,0\log 0 = 0
H(x)=−i=1∑npilogpiif pi=0,0log0=0
熵越大,随机变量的不确定性就越大。
条件熵
H
(
Y
∣
X
)
H(Y|X)
H(Y∣X)表示在已知随机变量
X
X
X的条件下随机变量
Y
Y
Y的不确定性
H
(
Y
∣
X
)
=
∑
i
=
1
n
p
i
H
(
Y
∣
X
=
x
i
)
p
i
=
P
(
X
=
x
i
)
,
i
=
1
,
2
,
…
,
n
(1)
H(Y|X) = \sum_{i=1}^np_iH(Y|X=x_i) \qquad p_i=P(X=x_i),i=1,2,\dots,n \tag{1}
H(Y∣X)=i=1∑npiH(Y∣X=xi)pi=P(X=xi),i=1,2,…,n(1)
“信息增益”:特征
A
A
A对训练数据集
D
D
D的信息增益
g
(
D
,
A
)
g(D,A)
g(D,A),定义为集合
D
D
D的经验熵
H
(
D
)
H(D)
H(D)与特征
A
A
A给定条件下
D
D
D的经验条件熵
H
(
D
∣
A
)
H(D|A)
H(D∣A)之差,即
g
(
D
,
A
)
=
H
(
D
)
−
H
(
D
∣
A
)
(2)
g(D,A) = H(D) - H(D|A) \tag{2}
g(D,A)=H(D)−H(D∣A)(2)
我们把训练数据集
D
D
D的样本容量设为
∣
D
∣
|D|
∣D∣,设有
K
K
K个类
D
k
,
k
=
1
,
2
,
…
,
K
,
∣
D
k
∣
D_k, \ k=1,2,\dots,K, \ |D_k|
Dk, k=1,2,…,K, ∣Dk∣为属于类
D
k
D_k
Dk的样本个数,训练数据集
D
D
D的熵为:
H
(
D
)
=
−
∑
i
=
1
K
∣
D
k
∣
∣
D
∣
log
2
∣
D
k
∣
∣
D
∣
(3)
H(D) = - \sum_{i=1}^K \frac{|D_k|}{|D|}\log_2 \frac{|D_k|}{|D|} \tag{3}
H(D)=−i=1∑K∣D∣∣Dk∣log2∣D∣∣Dk∣(3)
设特征
A
A
A有
n
n
n个不同的取值
{
a
1
,
a
2
,
…
,
a
n
}
\{a_1,a_2,\dots,a_n \}
{a1,a2,…,an},根据特征
A
A
A的取值将
D
D
D划分为
n
n
n个子集
D
1
,
D
2
,
…
,
D
n
D_1,D_2,\dots,D_n
D1,D2,…,Dn,
∣
D
i
∣
|D_i|
∣Di∣为
D
i
D_i
Di的样本个数,记子集
D
i
D_i
Di中属于类
D
k
D_k
Dk的样本集合为
D
i
k
D_{ik}
Dik,
∣
D
i
k
∣
|D_{ik}|
∣Dik∣为
D
i
k
D_{ik}
Dik的样本个数,那么经验条件熵
H
(
D
∣
A
)
H(D|A)
H(D∣A)为:
H
(
D
∣
A
)
=
∑
i
=
1
n
∣
D
i
∣
∣
D
∣
H
(
D
i
)
=
−
∑
i
=
1
n
∣
D
i
∣
∣
D
∣
∑
k
=
1
K
∣
D
i
k
∣
∣
D
i
∣
log
2
∣
D
i
k
∣
∣
D
i
∣
(4)
H(D|A) = \sum_{i=1}^n \frac{|D_i|}{|D|}H(D_i) = -\sum_{i=1}^n \frac{|D_i|}{|D|} \sum_{k=1}^K \frac{|D_{ik}|}{|D_i|} \log_2 \frac{|D_{ik}|}{|D_i|} \tag{4}
H(D∣A)=i=1∑n∣D∣∣Di∣H(Di)=−i=1∑n∣D∣∣Di∣k=1∑K∣Di∣∣Dik∣log2∣Di∣∣Dik∣(4)
信息增益为:
g
(
D
,
A
)
=
−
∑
i
=
1
K
∣
D
k
∣
∣
D
∣
log
∣
D
k
∣
∣
D
∣
+
∑
i
=
1
n
∣
D
i
∣
∣
D
∣
∑
k
=
1
K
∣
D
i
k
∣
∣
D
i
∣
log
2
∣
D
i
k
∣
∣
D
i
∣
(5)
g(D,A) = - \sum_{i=1}^K \frac{|D_k|}{|D|}\log \frac{|D_k|}{|D|} + \sum_{i=1}^n \frac{|D_i|}{|D|} \sum_{k=1}^K \frac{|D_{ik}|}{|D_i|} \log_2 \frac{|D_{ik}|}{|D_i|} \tag{5}
g(D,A)=−i=1∑K∣D∣∣Dk∣log∣D∣∣Dk∣+i=1∑n∣D∣∣Di∣k=1∑K∣Di∣∣Dik∣log2∣Di∣∣Dik∣(5)
经验熵 H ( D ) H(D) H(D)表示对数据集 D D D进行分类的不确定性。而经验条件熵 H ( D ∣ A ) H(D|A) H(D∣A)表示在特征 A A A给定的条件下对数据集 D D D进行分类的不确定性。那么他们的差,即信息增益,就表示由于特征 A A A而使得对数据集 D D D的分类的不确定性减少的程度。信息增益大的特征具有更强的分类能力。
信息增益算法
(1)输入训练数据集D和特征A
(2)计算数据集D的经验熵
H
(
D
)
H(D)
H(D)
H
(
D
)
=
−
∑
i
=
1
K
∣
D
k
∣
∣
D
∣
log
∣
D
k
∣
∣
D
∣
H(D) = - \sum_{i=1}^K \frac{|D_k|}{|D|}\log \frac{|D_k|}{|D|}
H(D)=−i=1∑K∣D∣∣Dk∣log∣D∣∣Dk∣
(3)计算特征
A
A
A对数据集
D
D
D的经验条件熵
H
(
D
∣
A
)
H(D|A)
H(D∣A)
H
(
D
∣
A
)
=
−
∑
i
=
1
n
∣
D
i
∣
∣
D
∣
∑
k
=
1
K
∣
D
i
k
∣
∣
D
i
∣
log
2
∣
D
i
k
∣
∣
D
i
∣
H(D|A) = -\sum_{i=1}^n \frac{|D_i|}{|D|} \sum_{k=1}^K \frac{|D_{ik}|}{|D_i|} \log_2 \frac{|D_{ik}|}{|D_i|}
H(D∣A)=−i=1∑n∣D∣∣Di∣k=1∑K∣Di∣∣Dik∣log2∣Di∣∣Dik∣
(4)计算信息增益
g
(
D
,
A
)
=
H
(
D
)
−
H
(
D
∣
A
)
g(D,A) = H(D) - H(D|A)
g(D,A)=H(D)−H(D∣A)
(5)输出特征
A
A
A对训练集
D
D
D的信息收益
g
(
D
,
A
)
g(D,A)
g(D,A)
举个例子说明一下
贷款申请样本数据,这个数据我们引用自李航的《统计学习方法》,我们根据信息增益准则选择最优特征。
-
首先我们计算经验熵 H ( D ) H(D) H(D)
我们最后要决策的是“是否同意贷款申请”,所以我们是二分类问题,根据“类别”这一列,我们可以统计出来“是”的概率分布为 9 15 \dfrac{9}{15} 159,“否”的概率分布为 6 15 \dfrac{6}{15} 156。那我们可以求出经验熵 H ( D ) H(D) H(D):
H ( D ) = − 9 15 log 2 9 15 − 6 15 log 2 6 15 = 0.971 H(D) = -\frac{9}{15} \log_2 \frac{9}{15} -\frac{6}{15} \log_2 \frac{6}{15} = 0.971 H(D)=−159log2159−156log2156=0.971 -
计算各特征对数据集 D D D的信息增益。这里我们可以从表格中知道有4个特征,分别是“年龄”,“有工作”,“有自己的房子”和“信贷情况”。
(1)对于“年龄”特征
对于“年龄”这一特征分别有“青年”,“中年”,“老年”这三种情况,并且概率分布都为 5 15 \dfrac{5}{15} 155。“青年”、"中年"和“老年”在“类别”为是的比例分别为 2 5 \dfrac{2}{5} 52, 3 5 \dfrac{3}{5} 53, 4 5 \dfrac{4}{5} 54。“年龄”特征对数据集 D D D的信息增益为:
g ( D , 年 龄 ) = H ( D ) − [ 5 15 H ( 青 年 ) + 5 15 H ( 中 年 ) + 5 15 H ( 老 年 ) ] = 0.971 − [ 5 15 ( − 2 5 log 2 2 5 − 3 5 log 2 3 5 ) + 5 15 ( − 3 5 log 2 3 5 − 2 5 log 2 2 5 ) + 5 15 ( − 4 5 log 2 4 5 − 1 5 log 2 1 5 ) ] = 0.971 − 0.888 = 0.083 \begin{aligned} g(D, 年龄) &= H(D) - [\frac{5}{15}H(青年)+\frac{5}{15}H(中年)+\frac{5}{15}H(老年)] \\ &= 0.971 - [\frac{5}{15}(-\frac{2}{5} \log_2 \frac{2}{5} - \frac{3}{5} \log_2 \frac{3}{5}) + \frac{5}{15}(-\frac{3}{5} \log_2 \frac{3}{5} - \frac{2}{5} \log_2 \frac{2}{5}) \\& + \frac{5}{15}(-\frac{4}{5} \log_2 \frac{4}{5} - \frac{1}{5} \log_2 \frac{1}{5})] \\ &=0.971 - 0.888 \\ &= 0.083 \end{aligned} g(D,年龄)=H(D)−[155H(青年)+155H(中年)+155H(老年)]=0.971−[155(−52log252−53log253)+155(−53log253−52log252)+155(−54log254−51log251)]=0.971−0.888=0.083
(2)类似的,我们可以求出其他特征的信息增益,这里我们分别将“有工作”,“有自己的房子”和“信贷情况”设为 A 2 , A 3 , A 4 A_2,A_3,A_4 A2,A3,A4,其样本子集设为 D 1 , D 2 , D 3 D_1,D_2,D_3 D1,D2,D3。
g ( D , A 2 ) = H ( D ) − [ 5 15 H ( D 1 ) + 10 15 H ( D 2 ) ] = 0.971 − [ 5 15 × 0 + 10 15 ( − 4 10 log 2 4 10 − 6 10 log 2 6 10 ) ] = 0.324 \begin{aligned} g(D,A_2) &= H(D) - [\frac{5}{15}H(D_1) + \frac{10}{15}H(D_2)] \\ & = 0.971 - [\frac{5}{15} \times 0 + \frac{10}{15}(-\frac{4}{10} \log_2 \frac{4}{10} - \frac{6}{10} \log_2 \frac{6}{10})] \\ &= 0.324 \end{aligned} g(D,A2)=H(D)−[155H(D1)+1510H(D2)]=0.971−[155×0+1510(−104log2104−106log2106)]=0.324
g ( D , A 3 ) = 0.971 − [ 6 15 × 0 + 9 15 ( − 3 9 log 2 3 9 − 6 9 log 2 6 9 ) ] = 0.971 − 0.551 = 0.420 \begin{aligned} g(D,A_3) &= 0.971 - [\frac{6}{15} \times 0 + \frac{9}{15}(-\frac{3}{9} \log_2 \frac{3}{9} - \frac{6}{9} \log_2 \frac{6}{9})] \\ &= 0.971 - 0.551 \\ &= 0.420 \\ \end{aligned} g(D,A3)=0.971−[156×0+159(−93log293−96log296)]=0.971−0.551=0.420
g
(
D
,
A
4
)
=
0.971
−
[
5
15
(
−
1
5
log
2
1
5
−
4
5
log
2
4
5
)
+
6
15
(
−
4
6
log
2
4
6
−
2
6
log
2
2
6
)
+
4
15
(
−
6
6
log
2
6
6
−
0
log
2
0
)
]
=
0.363
\begin{aligned} g(D,A_4) &= 0.971 - [\frac{5}{15}(-\frac{1}{5}\log_2\frac{1}{5}-\frac{4}{5}\log_2\frac{4}{5}) \\&+ \frac{6}{15}(-\frac{4}{6}\log_2\frac{4}{6}-\frac{2}{6}\log_2\frac{2}{6}) + \frac{4}{15}(-\frac{6}{6}\log_2\frac{6}{6}-0\log_20)] \\&= 0.363 \end{aligned}
g(D,A4)=0.971−[155(−51log251−54log254)+156(−64log264−62log262)+154(−66log266−0log20)]=0.363
(3)最后,比较各特征的信息增益值。因为信息增益值大的特征更具有分类能力,由于特征
A
3
A_3
A3(有自己的房子)的信息增益值最大,所以选择特征
A
3
A_3
A3作为最优特征。
信息增益比
特征
A
A
A对训练数据集
D
D
D的信息增益比
g
R
(
D
,
A
)
g_R(D,A)
gR(D,A)定义为其信息增益
g
(
D
,
A
)
g(D,A)
g(D,A)与训练数据集
D
D
D的经验熵
H
(
D
)
H(D)
H(D)之比:
g
R
(
D
,
A
)
=
g
(
D
,
A
)
H
(
D
)
g_R(D,A)=\frac {g(D,A)}{H(D)}
gR(D,A)=H(D)g(D,A)
实际上,信息增益对可取值数目较多的属性有所偏好,为减少这种偏好可能带来的不利影响,我们使用“信息增益比”也就是“增益率”来选择最优划分属性。信息增益比对可取值数目较少的属性有所偏好。
基尼指数
分类问题中,假设有
K
K
K个类,样本点属于第
k
k
k类的概率为
p
k
p_k
pk,则概率分布的基尼指数定义为:
G
i
n
i
(
p
)
=
∑
k
=
1
K
p
k
(
1
−
p
k
)
=
1
−
∑
k
=
1
K
p
k
2
Gini(p) = \sum_{k=1}^K p_k(1-p_k) = 1- \sum_{k=1}^K p_k^2
Gini(p)=k=1∑Kpk(1−pk)=1−k=1∑Kpk2
对于二分类问题,若样本中第1类的概率为
p
p
p,则概率分布的基尼指数为:
G
i
n
i
(
D
)
=
2
p
(
1
−
p
)
Gini(D) = 2p(1-p)
Gini(D)=2p(1−p)
如果样本集合
D
D
D根据特征
A
A
A是否取某一可能值
a
a
a被分割称
D
1
D_1
D1和
D
2
D_2
D2两个部分,即:
D
1
=
{
(
x
,
y
)
∈
D
∣
A
(
x
)
=
a
}
,
D
2
=
D
−
D
1
D_1 = \{ (x,y) \in D| A(x) = a\},\qquad D_2 = D - D_1
D1={(x,y)∈D∣A(x)=a},D2=D−D1
在特征
A
A
A的条件下,集合
D
D
D的基尼指数定义为
G
i
n
i
(
D
,
A
)
=
∣
D
1
∣
∣
D
∣
G
i
n
i
(
D
1
)
+
∣
D
2
∣
∣
D
∣
G
i
n
i
(
D
2
)
Gini(D,A) = \frac{|D_1|}{|D|}Gini(D_1)+ \frac{|D_2|}{|D|}Gini(D_2)
Gini(D,A)=∣D∣∣D1∣Gini(D1)+∣D∣∣D2∣Gini(D2)
ID3 算法
程序是根据上面的流程进行编写的,变量命名也根据公式来的。我们这里没有加入阈值,只是通过python对ID3算法进行简单的实现,并通过几个测试数据对决策树进行测试。
整个算法中,我们只用到了numpy
库,树的结构我们通过list
进行构建。
'''
因为我们的问题是二分类问题,所以我们树的结构应该是二叉树,
这里我们定义构造二叉树和对左右结点以及根结点进行赋值
其中`Bintree`为构建树,`set_root` `set_left` `set_right`为设置根结点和左右结点,
`root` `left` `right`为访问结点。
'''
def Bintree(data, left=None, right=None):
btree = [data, left, right]
return btree
# 设置结点
def set_root(btree, data):
btree[0] = data
def set_left(btree, left):
btree[1] = left
def set_right(btree, right):
btree[2] = right
# 读取结点
def root(btree):
return btree[0]
def left(btree):
return btree[1]
def right(btree):
return btree[2]
'''
对于ID3算法来说,最重要的就是求信息增益,
在代码中,尽量通过矩阵运算来减少for循环的使用,求信息增益的代码如下:
在代码中我们导入了numpy库,取了别名为np
import numpy as np
'''
def get_gain(data, features):
m = data.shape[0] # 数据样本数
n = data.shape[1] # 特征数
Ck = np.bincount(data[:, n-1])
Hd = round(-np.sum(Ck/m * np.log2(Ck/m)), 3) # 经验熵H(D)
gain = [] # 信息增益
'''
np.bincount 是用来统计矩阵中相同元素的个数.
features为特征集合,在程序中我们用0 1 2 3 来代表数据集中的四个特征
'''
for i in features:
Di = np.bincount(data[:, i]) # 特征子集的样本数
Hdi = []
for k in range(len(Di)):
index = np.where(data[:, i] == k)
Dik = np.bincount(data[:, n-1][index]) # 找出"类别"中为1的数量,样本子集中允许放贷的样本量
if Dik[0] == 0:
hdi = -(Dik[1]/Di[k] * np.log2(Dik[1]/Di[k]))
elif len(Dik) == 2 and Dik[1] == 0:
hdi = -(Dik[0]/Di[k] * np.log2(Dik[0]/Di[k]))
else:
hdi = -np.sum(Dik/Di[k] * np.log2(Dik/Di[k]))
Hdi.append(hdi)
Hda = round(np.sum(Di/m * Hdi), 3)
g = round(Hd - Hda, 3) # 计算信息增益
gain.append(g)
return gain
'''
建造决策树的函数
'''
def Buid_tree(data, features):
if len(features) != 0:
gain = get_gain(data, features)
gain = np.array([gain])
Max_index = np.where(gain == np.max(gain)) # 找到信息增益最大值的位置
# 最大信息增益的位置
x = Max_index[0].tolist()[0]
y = Max_index[1].tolist()[0]
btree = Bintree(features[y]) # 创建根结点
lable = [] # 标记是否对子集进行正确的分类
l_r = [] # 标记左叶结点还是右叶结点
inx = [] # 存储每个子特征的索引
for i in range(len(np.bincount(data[:, y]))): # 该特征下,每个子特征的分类情况
index = np.where(data[:, y] == i) # 每个子特征的索引
inx.append(index)
judge_mat = data[:, data.shape[1]-1][index] # 每个子特征是否进行放贷的情况
# 进行判断,添加标记,lable中1代表子集中都分类正确,0代表还有没有正确分类的子集
# l_r中1代表右结点,0代表左结点
if len(np.where(judge_mat == 1)[0]) == len(judge_mat):
lable.append(1)
l_r.append(1)
elif len(np.where(judge_mat == 0)[0]) == len(judge_mat):
lable.append(1)
l_r.append(0)
else:
lable.append(0)
l_r.append(None)
# 判断是否属于同一类,如果长度一样,说明里面只有一类
features.remove(features[y])
if len(np.where(np.array(lable) == 1)[0]) == len(lable):
set_left(btree, "No")
set_right(btree, "Yes")
elif len(np.where(np.array(lable) == 1)) != 0:
for i in range(len(lable)):
if lable[i] == 1 and l_r[i] == 0:
data = np.delete(data, inx[i], axis=0)
set_right(btree, Buid_tree(data, features))
set_left(btree, "No")
elif lable[i] == 1 and l_r[i] == 1:
data = np.delete(data, inx[i], axis=0)
set_right(btree, "Yes")
set_left(btree, Buid_tree(data, features))
else:
continue
else:
# 没有进行完全分类的情况
data1 = np.delete(data, inx[0], axis=0)
data2 = np.delete(data, inx[1], axis=0)
set_left(btree, Buid_tree(data1, features))
set_right(btree, Buid_tree(data2, features))
return btree
下面的部分代码是用来进行测试:
def search(test, tree):
for data in test:
btree = tree
while True:
feature = root(btree)
if data[feature] == 0:
btree = left(btree)
if isinstance(btree, str):
print(btree)
break
else:
btree = right(btree)
if isinstance(btree, str):
print(btree)
break
全部代码
import numpy as np
# 生成数据集
'''
年龄: 0-青年 1-中年 2-老年
有工作:0-否 1-是
有自己的房子 0-否 1-是
信贷情况 0-一般 1-好 2-非常好
类别 0-不放贷 1-放贷
本问题为二分类问题
'''
def Bintree(data, left=None, right=None):
btree = [data, left, right]
return btree
def set_root(btree, data):
btree[0] = data
def set_left(btree, left):
btree[1] = left
def set_right(btree, right):
btree[2] = right
def root(btree):
return btree[0]
def left(btree):
return btree[1]
def right(btree):
return btree[2]
def get_gain(data, features):
m = data.shape[0] # 数据样本数
n = data.shape[1] # 特征数
Ck = np.bincount(data[:, n-1])
Hd = round(-np.sum(Ck/m * np.log2(Ck/m)), 3) # 经验熵H(D)
gain = [] # 信息增益
for i in features:
Di = np.bincount(data[:, i]) # 特征子集的样本数
Hdi = []
for k in range(len(Di)):
index = np.where(data[:, i] == k)
Dik = np.bincount(data[:, n-1][index]) # 找出"类别"中为1的数量,样本子集中允许放贷的样本量
if Dik[0] == 0:
hdi = -(Dik[1]/Di[k] * np.log2(Dik[1]/Di[k]))
elif len(Dik) == 2 and Dik[1] == 0:
hdi = -(Dik[0]/Di[k] * np.log2(Dik[0]/Di[k]))
else:
hdi = -np.sum(Dik/Di[k] * np.log2(Dik/Di[k]))
Hdi.append(hdi)
Hda = round(np.sum(Di/m * Hdi), 3)
g = round(Hd - Hda, 3) # 计算信息增益
gain.append(g)
return gain
def Buid_tree(data, features):
if len(features) != 0:
gain = get_gain(data, features)
gain = np.array([gain])
Max_index = np.where(gain == np.max(gain)) # 找到信息增益最大值的位置
# 最大信息赠饮的位置
x = Max_index[0].tolist()[0]
y = Max_index[1].tolist()[0]
btree = Bintree(features[y]) # 创建根结点
lable = []
l_r = [] # 标记左叶结点还是右叶结点
inx = []
for i in range(len(np.bincount(data[:, y]))): # 该特征下,每个子特征的分类情况
index = np.where(data[:, y] == i) # 每个子特征的索引
inx.append(index)
judge_mat = data[:, data.shape[1]-1][index] # 每个子特征是否进行放贷的情况
if len(np.where(judge_mat == 1)[0]) == len(judge_mat): # 分类都为0的情况
lable.append(1)
l_r.append(1)
elif len(np.where(judge_mat == 0)[0]) == len(judge_mat): # 分类都为1的情况
lable.append(1)
l_r.append(0)
else:
lable.append(0)
l_r.append(None)
# 判断是否属于同一类,如果长度一样,说明里面只有一类
features.remove(features[y])
if len(np.where(np.array(lable) == 1)[0]) == len(lable): # 判断是否都分到一类中
set_left(btree, "No")
set_right(btree, "Yes")
elif len(np.where(np.array(lable) == 1)) != 0: # 其中有都分到一类的情况
for i in range(len(lable)):
if lable[i] == 1 and l_r[i] == 0:
data = np.delete(data, inx[i], axis=0)
set_right(btree, Buid_tree(data, features)) # 建立右子树
set_left(btree, "No") # 建立叶子节点
elif lable[i] == 1 and l_r[i] == 1:
data = np.delete(data, inx[i], axis=0)
set_right(btree, "Yes") # 建立叶子节点
set_left(btree, Buid_tree(data, features)) # 建立左子树
else:
continue
else: # 都没有分类到一类中
data1 = np.delete(data, inx[0], axis=0)
data2 = np.delete(data, inx[1], axis=0)
set_left(btree, Buid_tree(data1, features))
set_right(btree, Buid_tree(data2, features))
return btree
def search(test, tree):
for data in test:
btree = tree
while True:
feature = root(btree)
if data[feature] == 0:
btree = left(btree)
if isinstance(btree, str): # 因为叶子节点是“Yes”、“No”所以判断是否为字符类型
print(btree) # 输出叶子结点
break
else:
btree = right(btree)
if isinstance(btree, str):
print(btree)
break
if __name__ == '__main__':
data_set = np.array([[0, 0, 0, 0, 0],
[0, 0, 0, 1, 0],
[0, 1, 0, 1, 1],
[0, 1, 1, 0, 1],
[0, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[1, 0, 0, 1, 0],
[1, 1, 1, 1, 1],
[1, 0, 1, 2, 1],
[1, 0, 1, 2, 1],
[2, 0, 1, 2, 1],
[2, 0, 1, 1, 1],
[2, 1, 0, 1, 1],
[2, 1, 0, 2, 1],
[2, 0, 0, 0, 0]])
features = np.arange(0, data_set.shape[1]-1).tolist() # 特征集合
Tree = Buid_tree(data_set, features)
# 测试数据集
test_data = np.array([[1, 1, 1, 2],
[2, 1, 0, 1],
[1, 0, 0, 0]])
search(test_data, Tree)
'''
输出结果自行测试喽,自己编写的程序可以跑通,这个程序只是简单的实现决策树,
没有进行优化,更换数据集可能会报错。
'''
参考文献:
【1】李航 《统计学习方法》
【2】周志华《机器学习》