连续值处理:
决策树处理的是离散数据,当离散数据中混杂着连续数据时,我们希望将连续数据离散化。

我们可以将连续取值的值域划分为多个区间,每个区间视为一个属性取值,这样就将连续数据离散化了。

例如:属性a的取值范围为0-100,我们划分为0-25, 25-50, 50-75, 75-100四个区间,作为属性a的四种取值

那么,划分边界的选择就是我们需要解决的问题。我们以二分为例(化为两个子区间):

设一个属性a={a1, a2, ..., an}的所有取值:

(0.697, 0.774, 0.634, 0.608, 0.556, 0.403, 0.481, 0.437, 0.666, 0.243, 0.245, 0.343, 0.639, 0.657, 0.360, 0.593, 0.719)

将属性a的所有的取值进行排序:

(0.774, 0.719, 0.697, 0.666, 0.657, 0.639, 0.634, 0.608, 0.593, 0.556, 0.481, 0.437, 0.403, 0.36, 0.343, 0.245, 0.243)

我们可以取任意相邻取值的中位点,作为划分点,例如:我们可以使用0.634和0.608的中位点进行划分,我们希望划分之后的数据集更加纯净,所以我们依然用信息熵来度量划分的效果,我们选取划分效果最好的点作为划分点。

候选的n-1个中位点:

求按照每个中位点划分的信息熵,信息熵最小的就是最优划分点

我们以西瓜数据集,来实现以下:

色泽	根蒂	敲声	纹理	脐部	触感	密度	含糖率	好瓜
青绿	蜷缩	浊响	清晰	凹陷	硬滑	0.697	0.46	是
乌黑	蜷缩	沉闷	清晰	凹陷	硬滑	0.774	0.376	是
乌黑	蜷缩	浊响	清晰	凹陷	硬滑	0.634	0.264	是
青绿	蜷缩	沉闷	清晰	凹陷	硬滑	0.608	0.318	是
浅白	蜷缩	浊响	清晰	凹陷	硬滑	0.556	0.215	是
青绿	稍蜷	浊响	清晰	稍凹	软粘	0.403	0.237	是
乌黑	稍蜷	浊响	稍糊	稍凹	软粘	0.481	0.149	是
乌黑	稍蜷	浊响	清晰	稍凹	硬滑	0.437	0.211	是
乌黑	稍蜷	沉闷	稍糊	稍凹	硬滑	0.666	0.091	否
青绿	硬挺	清脆	清晰	平坦	软粘	0.243	0.267	否
浅白	硬挺	清脆	模糊	平坦	硬滑	0.245	0.057	否
浅白	蜷缩	浊响	模糊	平坦	软粘	0.343	0.099	否
青绿	稍蜷	浊响	稍糊	凹陷	硬滑	0.639	0.161	否
浅白	稍蜷	沉闷	稍糊	凹陷	硬滑	0.657	0.198	否
乌黑	稍蜷	浊响	清晰	稍凹	软粘	0.36	0.37	否
浅白	蜷缩	浊响	模糊	平坦	硬滑	0.593	0.042	否
青绿	蜷缩	沉闷	稍糊	稍凹	硬滑	0.719	0.103	否

读取数据:

def loaddataset(filename):
	dataset = []
	fp = open(filename)
	for i in fp.readlines():
		a = i.strip().split()
		dataset.append(a)
	attribute_name = dataset.pop(0)
	del(attribute_name[-1])
	return dataset, attribute_name

dataset:

[['青绿', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '0.697', '0.46', '是'], ['乌黑', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '0.774', '0.376', '是'], ['乌黑', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '0.634', '0.264', '是'], ['青绿', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '0.608', '0.318', '是'], ['浅白', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '0.556', '0.215', '是'], ['青绿', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '0.403', '0.237', '是'], ['乌黑', '稍蜷', '浊响', '稍糊', '稍凹', '软粘', '0.481', '0.149', '是'], ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '硬滑', '0.437', '0.211', '是'], ['乌黑', '稍蜷', '沉闷', '稍糊', '稍凹', '硬滑', '0.666', '0.091', '否'], ['青绿', '硬挺', '清脆', '清晰', '平坦', '软粘', '0.243', '0.267', '否'], ['浅白', '硬挺', '清脆', '模糊', '平坦', '硬滑', '0.245', '0.057', '否'], ['浅白', '蜷缩', '浊响', '模糊', '平坦', '软粘', '0.343', '0.099', '否'], ['青绿', '稍蜷', '浊响', '稍糊', '凹陷', '硬滑', '0.639', '0.161', '否'], ['浅白', '稍蜷', '沉闷', '稍糊', '凹陷', '硬滑', '0.657', '0.198', '否'], ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '0.36', '0.37', '否'], ['浅白', '蜷缩', '浊响', '模糊', '平坦', '硬滑', '0.593', '0.042', '否'], ['青绿', '蜷缩', '沉闷', '稍糊', '稍凹', '硬滑', '0.719', '0.103', '否']]
[Finished in 0.4s]

attribute_name:

['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '密度', '含糖率']
[Finished in 0.2s]

计算以某点划分的信息熵:

def calculate_EntD(dataset, n, m):
	#计算信息熵时,先将原连续数据改为字符串形式(<=m, >m)的离散数据
	dataset_new = copy.deepcopy(dataset)
	for i in range(len(dataset_new)):
		a = str(m)
		if float(dataset_new[i][n]) <= m:
			dataset_new[i][n] = '<='+a
		else:
			dataset_new[i][n] = '>'+a
	#这里计算信息熵的函数,直接使用决策树中的相关函数
	entd = EntD(dataset_new)
	count = statistics(dataset_new, n)
	entdv = EntDv(count)
	gaind = GainD(entd, entdv, len(dataset_new))
	#返回信息熵
	return gaind

我们在属性密度上用0.5作为划分,其划分后的信息熵为(密度属性是第七个属性,n=6):

gaind = calculate_EntD(dataset, 6, 0.5)
0.003585078590305768
[Finished in 0.2s]

根据相关信息修改原数据集:

def change_dataset(dataset):
	data = {}
	for i in dataset:
		for j in i:
			#筛选出连续的数值型属性时,使用差错控制方法,来判断字符串是否可以转化为数字
			try:
				float(j)
				if i.index(j) not in data:
					data[i.index(j)] = [float(j)]
				else:
					data[i.index(j)].append(float(j))
			except:
				continue
 
	#记录所有候选的中位点
	Ta_set = []
	for i in data:
		#对属性的所有取值排序
		a = sorted(data[i], reverse = True)
 
		#记录每个中位点划分的信息熵
		gaind_set = []
		for j in range(len(a)-1):
			Ta = (a[j] + a[j+1]) / 2
 
			#calculate_EntD计算每种划分的信息熵
			gaind_set.append(calculate_EntD(dataset, i, Ta))
 
		#选取信息熵最小的那个划分点
		b = min(gaind_set)
		k = gaind_set.index(b)
		Ta_set.append((a[k] + a[k+1]) / 2)
 
	#处理数据集,将连续数据替换为字符串形式(<=m, >m)的离散形式
	dataset_new = copy.deepcopy(dataset)
	n = 0
	for i in data:
		c = str(Ta_set[n])
		for j in range(len(dataset_new)):
			if float(dataset_new[j][i]) <= Ta_set[n]:
				dataset_new[j][i] = '<=' + c
			else:
				dataset_new[j][i] = '>' + c
		n += 1
	return dataset_new

返回的新数据集,就可以使用在决策树模型中,我们直接使用上一篇文章中的决策树模型算法,得到模型如下:

[['纹理清晰', ['根蒂蜷缩', '是'], ['根蒂稍蜷', ['含糖率<=0.2655', '是'], ['含糖率>0.2655', '否']], ['根蒂硬挺', '否']], ['纹理稍糊', ['触感软粘', '是'], ['触感硬滑', '否']], ['纹理模糊', '否']]
[Finished in 0.2s]