什么是决策树?
如果将决策树和上一章的分类器一起讲述,那么决策树这种算法也是用于对物品分类的,书有一个非常简单的例子,能帮助我理解什么是决策树。
给你一个水果,你可以通过以下方式判断出这是一个什么水果。
可以看出,决策树上就是一个又一个if-then的语句联系起来的。而且从最终结果,我们也能够看出整个推理的过程。而上一章讲述的贝叶斯分类器里每一个单词的重要性通过计算而得到的。
实例
背景
书中还是通过实例来带领我们学习。背景:一个网站,网站的功能被分为了基本或高级,网站被很多用户使用了一段时间。用户在使用过程中,我们统计了一下信息:
- 我们想知道哪些用户可能成为付费用户。
显然,如果高级功能(premium)是付费用户专属,那么我们就想知道哪种用户(来自哪里的,ip是哪的,浏览网页多少次的)更会使用高级功能,更可能成为我们的付费用户。比如:上图中,所有来自网站为google的用户都会使用premium高级功能,这样了,我们就知道应该加大在google上做广告的力度,因为google那边过来的用户更有可能使用高级功能,更可能成为我们的会员。
由此,注意两点:
- 使用决策树的时候,我们必然需要一些原始的数据对决策树进行训练,这些数据都包含了用户最终的选择、或者结果。也就是需要有输入和输出的数据。之前的博客也说过决策树属于监督类学习。
- 虽然在上面的例子中,我们能够直观的看出来自google的用户,更容易成为会员(看起来我们就已经找到了成为会员的关键)。但是实际情况不是那么简单,请继续看本博客就知道了。此外,如果原始数据特别多的时候,用肉眼观察是非常辛苦的。
数据集
书中为我们准备了数据集,就是上面那幅表的内容,用列表数组表示:
my_data=[['slashdot','USA','yes',18,'None'],
['google','France','yes',23,'Premium'],
['digg','USA','yes',24,'Basic'],
['kiwitobes','France','yes',23,'Basic'],
['google','UK','no',21,'Premium'],
['(direct)','New Zealand','no',12,'None'],
['(direct)','UK','no',21,'Basic'],
['google','USA','no',24,'Premium'],
['slashdot','France','yes',19,'None'],
['digg','USA','no',18,'None'],
['google','UK','no',18,'None'],
['kiwitobes','UK','no',19,'None'],
['digg','New Zealand','yes',12,'Basic'],
['slashdot','UK','no',21,'None'],
['google','UK','yes',18,'Basic'],
['kiwitobes','France','yes',19,'Basic']]
树中节点的表示
就像上面那个水果的例子一样,决策树实际上是由一个又有一个的节点组成的。在代码中,我们用一个类来表示:
#这是决策树的表达形式:一个一个的节点。每一个节点有五个属性。
class decisionnode:
def __init__(self,col=-1,value=None,results=None,tb=None,fb=None):
self.col=col #被判断条件的对应的列的索引号,如图中:来源网站,是否阅读过FAQ的列序号
self.value=value#什么情况下被判定为真?就看这个value的值,如果value为Yes(对应是否阅读过FAQ),也是Yes的时候为真。如果是大于20(对应浏览页数)那就是大于20为真
self.results=results#最终结果,只有叶节点才有这个值,分类结果或者判定的结果
self.tb=tb#也是一个decisionnode,t代表true,为true的话,就走这个节点。叶节点,没有这个,但是有结果
self.fb=fb#也是一个decisionnode,t代表false,为false的话,就走这个节点。叶节点,没有这个,但是有结果
构建树
我们分类回归树的算法(CART,Classification And Regression Tree)来构建树。该算法的思想是:首先创建一个根节点,然后我们选择所有统计到里面的数据的一个(就是中的一个)来对初始数据集进行划分。比如,根节点处,我们选择是否阅读过FAQ来对节点进行拆分,就可以拆分成"看过"和”没看过”的两个数据集,抽取两个数据集的最后的一列,也就是我们关心的用户使用的功能,由此可知是否是有可能成为付费会员。如下图所示:
所以,根据我们这个需求,代码我们写了如下函数:
分解节点
#根据某一列,对数据进行拆分成两个set,一个set代表选true的时候,一个set代表选false的时候
def divideset(rows,column,value):
#定义了一个新函数,用这个函数去判断每一行数据是属于第一组(true),还是第二组(false)
split_function=None
#根据value的值,如果value是数字的话,一般都是大于某个数,如果value是布尔的话,那就是为true。
#为了使这个函数既能够,又能够接受数值类的判断,又能够结果布尔值,是与否的判断,才如此的。
if isinstance(value,int) or isinstance(value,float):
split_function=lambda row:row[column]>=value#lambda创建一个新函数,该函数的接受的参数为row,函数内容为row[column]>=value
else:
split_function=lambda row:row[column]==value
#将数据集根据上面的函数,以及为真条件,判断,并返回
set1=[row for row in rows if split_function(row)]#用split_function函数判断一下,如果成功就是放在set1
set2=[row for row in rows if not split_function(row)]#用split_function函数判断一下,如果失败就是放在set2
return (set1,set2)
执行代码:
set1,set2=divideset(my_data,2,'yes')
print set1
print set2
结果:
>>>
[['slashdot', 'USA', 'yes', 18, 'None'], ['google', 'France', 'yes', 23, 'Premium'], ['digg', 'USA', 'yes', 24, 'Basic'], ['kiwitobes', 'France', 'yes', 23, 'Basic'], ['slashdot', 'France', 'yes', 19, 'None'], ['digg', 'New Zealand', 'yes', 12, 'Basic'], ['google', 'UK', 'yes', 18, 'Basic'], ['kiwitobes', 'France', 'yes', 19, 'Basic']]
[['google', 'UK', 'no', 21, 'Premium'], ['(direct)', 'New Zealand', 'no', 12, 'None'], ['(direct)', 'UK', 'no', 21, 'Basic'], ['google',