掰开揉碎机器学习系列-决策树(2)-CART决策树

一、CART决策树和ID3决策树的区别在哪里:

区别主要体现在对于连续型特征的处理方式,ID3决策树完全根据特征值等于几,做决策分支,这肯定容易出现很多很多分支,即所谓过拟合。

CART决策树在一定程度上缓解了这个问题,比如对于样本S的特征A的特征值1、2、3、4、5、6,如果是ID3决策树,那么可能会有6个分支,而对于CART决策树,只会有2个分支,如"特征A是否大于3.5",这也就是所谓CART决策树是一个二叉树,他对于连续型特征,只做二分切分。

CART和ID3的另一个区别是,CART使用基尼系数而不是熵,作为判断概率分布是否集中的标准。


二、CART决策树实现:

区别于ID3的主要是两个内容,一个是基尼系数,一个是连续型特征如何做分叉

1、基尼系数:

gini(A = AX) = 1 - sum(p(i) * p(i))

即特征A在取值AX时,1减去各个结果i的概率p(i)的乘积

基尼系数越小,反映概率分布越平稳,应优先作为分叉的特征。

2、连续型特征如何分叉:

对该特征A的所有特征值A1-AN排序,每两个特征值获取平均值,取得N-1个有序平均值,计算每个平均值的样本数据子集的基尼系数,取其中最小者,对所有特征均做此计算,基尼系数最小的特征及其最小基尼系数的平均值,作为分叉条件

如样本S的特征A的特征值1、2、3、4、5、6,并计算得平均值3,.5的样本数据子集的基尼系数最低,特征B的特征值1、2、3、4、5、6,并计算得平均值1,.5的样本数据子集的基尼系数最低,特征C的特征值1、2、3、4、5、6,并计算得平均值2,.5的样本数据子集的基尼系数最低.........最后得出最低基尼系数为特征A的平均值3.5,那么特征A是否大于3.5,作为分叉特征条件。

本例中,离散型特征同样计算基尼系数并和连续型特征一起参与比较,计算方式和ID3一样。

3、实现

#coding: utf8
from numpy import *
import pandas as pd
from math import log
import operator
from treeplot import *
import sys

def vote (dataset):
	map = {}
	for data in dataset:
		if map.has_key(data):
			map[data] += 1
		else:
			map[data] = 1
	max = -1
	best = 0
	for data in map:
		cnt = map[data]
		if max < cnt:
			max = cnt
			best = data
	
	return best

def calcgini (dataset):
	results = [data[-1] for data in dataset]
	map = {}
	for result in results:
		if map.has_key(result):
			map[result] += 1
		else:
			map[result] = 1
	gini = 1.0
	for result in map:
		cnt = map[result]
		p = float(cnt) / len(dataset)
		gini -= p * p
	return gini

def splitdata_disper (dataset, i, value):
	splitdata = []
	for data in dataset:
		curvalue = data[i]
		if curvalue == value:
			tmp = data[:i]
			tmp.extend(data[i + 1:])
			splitdata.append(tmp)
	return splitdata

def splitdata_contigus (dataset, i, value, flag):
	splitdata = []
	for data in dataset:
		curvalue = data[i]
		if flag == 0:
			if curvalue > value:
				leftpart = data[:i]
				leftpart.extend(data[i + 1:])
				splitdata.append(leftpart)
		else:
			if curvalue <= value:
				leftpart = data[:i]
				leftpart.extend(data[i + 1:])
				splitdata.append(leftpart)
	return splitdata

def getaverages (values):
	sorted_values = sorted(values)
	averages = []
	for i in range(len(sorted_values) - 1):
		average = float(sorted_values[i] + sorted_values[i + 1]) / 2
		averages.append(average)
	return averages

def get_type (data):
	if type(data).__name__ in ['float', 'int', 'long']:
		return 1
	else:
		return 0

def choose_bestfeature (dataset, labels):
	sum = len(dataset)
	bestgini = 100000.0
	bestfeatureidx = -1
	bestvalue_contigus = 0.0
	type = 0
	#遍历每一种特征
	for i in range(len(labels)):
		values = [data[i] for data in dataset]
		type = get_type(values[0])
		#如果是连续型,首先计算出所有连续特征值的排序后交叉均值
		#然后按这些均值为界,依次计算不同拆分结果的基尼系数,取基尼系数最小者
		if type == 1:
			averages = getaverages(values)
			mingini_contigus = 100000.0
			bestvalue = 0.0
			for average in averages:
				lesspart = splitdata_contigus(dataset, i, average, 0)
				lesspart_p = float(len(lesspart)) / sum
				leftpart_gini = lesspart_p * calcgini(lesspart)
				
				morepart = splitdata_contigus(dataset, i, average, 1)
				morepart_p = float(len(morepart)) / sum
				morepart_gini = morepart_p * calcgini(morepart)
				gini = leftpart_gini + morepart_gini
				
				if gini < mingini_contigus:
					mingini_contigus = gini
					bestvalue = average
			
			if mingini_contigus < bestgini:
				bestfeatureidx = i
				bestvalue_contigus = bestvalue
				type = 1
		#如果是离散型,直接取基尼系数最小者的特征
		else:
			mingini_disper = 100000.0
			values = set(values)
			for value in values:
				otherpart = splitdata_disper(dataset, i, value)
				p = float(len(otherpart)) / len(dataset)
				gini = p * calcgini(otherpart)
				if gini < mingini_disper:
					mingini_disper = gini
			if mingini_disper < bestgini:
				bestfeatureidx = i
				type = 0
				
	#如果是连续性特征作为最优特征,要做二值化,就是根据是否大于最优均值,将标签名称和数据,都做二值化
	#为什么要做二值化,不是三值化,四值化?因为标准CART决策树是一个二叉树,就是根据每个连续型特征是否大于某值来分叉
	if type == 1:
		labels[bestfeatureidx] = labels[bestfeatureidx] + " > " + str(bestvalue_contigus)
		for data in dataset:
			curvalue = data[bestfeatureidx]
			if data[bestfeatureidx] > bestvalue_contigus:
				data[bestfeatureidx] = 1
			else:
				data[bestfeatureidx] = 0
	
	return bestfeatureidx

def createtree (dataset, labels):
	results = [data[-1] for data in dataset]
	#只剩下一种结果时,
	if len(results) == results.count(results[0]):
		return results[0]
	#只剩下结果没有特征了
	if len(dataset[0]) == 1:
		return vote(results)
	
	bestfeatureidx = choose_bestfeature(dataset, labels)
	bestfeature = labels[bestfeatureidx]
	
	label = labels[bestfeatureidx]
	tree = {label:{}}
	
	del(labels[bestfeatureidx])
	bestfeaturevalues = set(data[bestfeatureidx] for data in dataset)
	for value in bestfeaturevalues:
		splitdata = splitdata_disper(dataset, bestfeatureidx, value)
		splitlabels = labels[:]
		if splitdata == []:
			continue
		tree[label][value] = createtree(splitdata, splitlabels)
	
	return tree

df = pd.read_table('nba.txt', sep = '\t')
#数据源,第一行是标题略过,第一列是球员名字略过
dataset = df.values[0:,1:].tolist()
labels = ['score', 'allscore', 'hitratio', 'threeratio', 'penaltyratio', 'time', 'count ', 'clublevel']
tree = createtree(dataset, labels)
createPlot(tree)

训练样本数据,以NBA球员当前的各方面数据(同时包括连续型和离散型特征),以及自设定的其球员档次等级:

name	score	allscore	hitratio	threeratio	penaltyratio	time	count	clublevel	tag
安东尼-戴维斯	31.6	537	51.9	28.2	81.3	37.2	17	Middle	1.5
拉塞尔-威斯布鲁克	31.2	561	43.5	33.7	81.3	35.4	18	Middleupper	1.5
德玛尔-德罗赞	30.2	483	48.8	25.8	80.9	36.8	16	Middle	2
詹姆斯-哈登	28.9	491	45.9	36.3	83.4	37	17	Middleupper	1.5
德马库斯-考辛斯	28.3	481	47.2	40.8	76.9	33.7	17	Middle	1.5
达米安-利拉德	28.2	536	46.3	36.6	89.6	35.7	19	Middle	2
凯文-杜兰特	27.2	462	57.4	44.6	83.8	34.4	17	upper	1
斯蒂芬-库里	26.7	454	50.2	42.5	92.9	33.3	17	upper	1
伊赛亚-托马斯	26.1	417	43.2	33.6	87.6	33.1	16	lower	2.5
吉米-巴特勒	25.8	413	49	42.6	88.7	35.2	16	Middleupper	1.5
考瓦伊-莱昂纳德	24.8	421	46.2	38.6	92.5	34.2	17	upper	1.5
凯里-欧文	24.8	372	49	43.3	84	33.9	15	upper	2
肯巴-沃克	24.5	392	46.9	41.1	79.6	33.4	16	Middle	2.5
勒布朗-詹姆斯	23.6	331	49.8	36.1	74.2	36.1	14	upper	1
安德鲁-维金斯	23.5	376	43	39.1	76.7	36.4	16	Middle	2.5
约翰-沃尔	23.5	305	45.4	38.1	81.6	34.2	13	Middle	2
卡梅罗-安东尼	23	368	45.4	34.7	86.8	33.8	16	Middle	1.5
凯文-勒夫	22.3	335	45.4	43	86.5	31.7	15	upper	1.5
C.J.-迈克科伦姆	22.3	424	47.3	44.5	91	34.6	19	Middle	2.5
吉安尼斯-安特托孔波	22	330	51.4	18.8	78.7	34.7	15	Middle	2
卡尔-安东尼-唐斯	21.1	338	49.1	39.4	71	34.7	16	Middle	2
布雷克-格里芬	21.1	380	48.7	22.2	77.4	33.1	18	upper	1.5
乔治-希尔	20.9	188	55.2	47.1	86.7	32.7	9	Middle	2
克里斯塔普斯-波尔津吉斯	20.9	335	49	40	78.6	33.3	16	Middle	2.5
哈里森-巴恩斯	20.8	332	46	28.6	87.1	37.3	16	Middle	2.5
保罗-乔治	20.8	249	43.9	37.2	93	34.9	12	Middleupper	1.5

运行结果如下:可以发现分叉还是比较多。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值