python编程实现决策树算法

这篇具有很好参考价值的文章主要介绍了python编程实现决策树算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

最近布置了个课堂作业,用python实现决策树算法 。整了几天勉勉强强画出了棵歪脖子树,记录一下。

大体思路:

1.创建决策树My_Decision_Tree类,类函数__init__()初始化参数、fit()进行决策树模型训练、predict()进行预测、evaluate()进行模型评估、save_model()保存模型(csv格式)、load_model()加载模型、show_tree()使用Pillow库绘制决策树以及其他一些封装的功能函数;

2.最佳划分点的度量通常有Gini值、Entropy、Classification error等,本程序采用Gini值,计算方法如下: 

python编程实现决策树算法

构建决策数模型所用到的功能函数(gini值计算相关)说明: 

①get_col_gini(self,threshold_point, value_series, label_series):计算一列数据在某一阈值划分点下的gini_split值;

②get_best_point(self,value_series, label_series):对连续型数据进行大小排序,分别计算切分点及其对应的gini_split值,找到使得gini_split值最小的最佳切分点;

③get_best_column(self,data,label_series):遍历数据表中的属性列,计算每列的最佳划分点和gini_split值,并对比找到最适合划分的一列及其对应的划分点;

3.决策树构建(训练)的逻辑: 

①程序开始以root为父节点,先找到6列属性(剩余属性)中最适合划分的列和划分点,并将其划分为两个子节点,记录判断条件和此节点位置。删除子节点中的该列属性以避免最终决策树模型倾向于某个属性,利用剩余的属性继续构造决策树;

②判断各个子节点中的标签是否相同,如果为同一类则移到叶节点中,否则将此节点更新到下个流程中的父节点中;

③一直循环以上过程,当父节点为空时结束训练,如果所有子节点都为叶节点时则决策树完美将数据分类;当然也会出现所有属性都用完但子节点中标签依旧不唯一的情况,这时以该节点中个数较多的标签类作为分类结果,终止模型构建。

# -*- coding: utf-8 -*-
# @Version: Python 3.8.2
# @Author: 707
# @Use: 决策树算法编写

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split  #数据集划分
#采用Pillow可视化决策树
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont

###决策树框架###
class My_Decision_Tree(object):
	'''决策树框架'''

	def __init__(self, arg=None):
		##初始化类参数
		self.arg = arg

		#存放决策树中的各层判断条件
		self.decision_df=pd.DataFrame(columns=['parent_depth','parent_pos','this_depth','this_pos','column','point','label'])
		self.Parent_node_list=[]	#存放父节点和子节点的DataFrame
		self.Child_node_list=[]	
		self.leaf_list=[] #存放划分好的叶节点
		self.branch_list=[]	#存放未能划分出来的节点

	def fit(self,x_train,y_train):
		'''传入训练集和标签构造决策树'''

		'''		
		程序逻辑:
		(1)程序开始以root为父节点,先找到6列属性(剩余属性)中最适合划分的列和划分点,并将其划分为两个子节点,
		记录判断条件和此节点位置。删除子节点中的该列属性以避免最终决策树模型倾向于某个属性,利用剩余的属性继续构造决策树
		(2)判断各个子节点中的标签是否相同,如果为同一类则移到叶节点中,否则将此节点更新到下个流程中的父节点中
		(3)一直循环以上过程,当父节点为空时结束训练,如果所有子节点都为叶节点时则决策树完美将数据分类;当然也会出现所有属性都用完但子节点
		中标签依旧不唯一的情况,这时以该节点中个数较多的标签类作为分类结果,终止模型构建。

		'''

		x_data=x_train.copy()
		if(y_train.name not in x_data.columns):
			x_data[y_train.name]=y_train
		
		#把第一层(原数据)放入父节点列表Parent_node_list
		self.Parent_node_list.append(x_data)
		#写入第一层的决策(跟节点)
		decision={'this_depth':1,'this_pos':0,'column':'root','label':'#'}
		self.decision_df=self.decision_df.append(decision,ignore_index=True)
		#开始循环计算分类节点
		parent_count=0 #循环的父节点数
		child_pos=0  #子节点的位置
		depth=2 #第几层节点
		while True:
			parent_node=self.Parent_node_list[parent_count]
			#找到第一个适合划分的列和划分点
			col_1,point_1=self.get_best_column(parent_node,parent_node[y_train.name])
			print('decision condition:',col_1,point_1)

			#根据条件把父节点划分为两部分
			Child_node1=parent_node.loc[parent_node[col_1]<=point_1]
			Child_node2=parent_node.loc[parent_node[col_1]>point_1]
			#每一部分的分类结果
			result=[]
			for child_node in [Child_node1,Child_node2]:
				#删除已使用过的属性
				del(child_node[col_1])

				#判断子节点标签是否为一类,是则将其放入叶节点列表中
				if(len(child_node[y_train.name].unique())==1):
					self.leaf_list.append(child_node)
					print('添加一个叶节点,标签类型:',child_node[y_train.name].unique()[0],'数据大小:',child_node.shape)
					result.append(child_node[y_train.name].unique()[0])

				# 判断子节点标签是否还有剩余属性可以作为分类依据,如果有则添加到子节点列表中用于后续分类
				elif(child_node.shape[1]!=1):
					self.Child_node_list.append(child_node)
					print('添加一个子节点,数据大小:',child_node.shape)
					result.append('#')
				#都不满足说明该节点没有分完但是分不下去了,提示错误
				else:
					self.branch_list.append(child_node)
					print('child_node节点已用完所有属性,仍然没分出来,剩余数据大小:')
					print(child_node[y_train.name].value_counts())

					values=child_node[y_train.name].value_counts()
					if(len(values)==0):
						replace=list(parent_node[y_train.name].value_counts().index)[0]
					else:
						replace=list(values.index)[0]
					print('用%s作为该条件下的预测结果' %replace)
					result.append(replace)

			#找到该父节点在该层中所对应的位置
			p_pos_list=self.decision_df.loc[(self.decision_df['this_depth']==depth-1)&(self.decision_df['label']=='#'),'this_pos']
			p_pos_list=list(p_pos_list)
			# print('p_pos_list:',p_pos_list)

			#判断完一个父节点之后,把判断条件加入decision_df中  
			decision1={'parent_depth':depth-1,'parent_pos':p_pos_list[parent_count],'this_depth':depth,'this_pos':child_pos,
						'column':col_1,'point':point_1,'label':result[0]}
			decision2={'parent_depth':depth-1,'parent_pos':p_pos_list[parent_count],'this_depth':depth,'this_pos':child_pos+1,
						'column':col_1,'point':point_1,'label':result[1]}
			self.decision_df=self.decision_df.append([decision1,decision2],ignore_index=True)

			#当遍历完父节点列表所有值后,将子节点更新为父节点
			child_pos+=2
			parent_count+=1
			if(parent_count==len(self.Parent_node_list)):
				parent_count=0
				child_pos=0
				depth+=1
				print('该层决策结束,进行下一层决策\n')
				self.Parent_node_list=self.Child_node_list.copy()
				self.Child_node_list.clear()
				print('更新parent_node_list,大小:%d' %len(self.Parent_node_list))

				#判断父节点列表中是否还有未分类的节点,如果没有则表示已经全部分好,结束训练
				if(len(self.Parent_node_list)==0):
					print('决策树构建完成')
					#显示构建好的决策树:判断条件及结果(叶节点)
					print(self.decision_df)

					break

	def predict(self,x_test):
		'''输入测试数据进行决策判断'''
		y_predict=list()
		
		if(type(x_test)==pd.core.series.Series):
			pred=self.get_ylabel(x_test)
			y_predict.append(pred)
		else:
			for index,row in x_test.iterrows():
				pred=self.get_ylabel(row)
				y_predict.append(pred)

		y_predict=np.array(y_predict,dtype=str)
		return y_predict



	def evaluate(self,x_test,y_test):
		'''输入测试集和标签评估决策树准确性,返回acc'''
		y_true=np.array(y_test,dtype=str)
		y_pred=self.predict(x_test)
		# print(y_pred)
		# print(y_true)
		label_list=list(self.decision_df['label'].unique())
		label_list.remove('#')
		label_list=np.array(label_list,dtype=str) #类型转换
		#创建混淆矩阵(index为true,columns为pred)
		confusion_matrix=pd.DataFrame(data=0,columns=label_list,index=label_list)
		for i in range(len(y_true)):
			confusion_matrix.loc[y_true[i],y_pred[i]]+=1
		print('混淆矩阵:')
		print(confusion_matrix)
		#计算准确率
		acc=0
		for i in range(len(label_list)):
			acc+=confusion_matrix.iloc[i,i]
		acc/=len(y_true)
		print('acc:%.5f' %acc)
		return acc

	def save_model(self,path):
		'''以csv格式保存模型'''
		self.decision_df.to_csv(path,index=False)

	def load_model(self,path):
		'''以csv格式读取模型'''
		self.decision_df=pd.read_csv(path)

	def get_col_gini(self,threshold_point, value_series, label_series):
	    '''Gini值计算函数'''

	    # 将输入进行重组
	    df_input = pd.DataFrame()
	    df_input['value'] = value_series
	    df_input['label'] = label_series
	    # print(df_input)
	    # 设计Gini值的计算表格
	    label_cols = label_series.value_counts()
	    df_gini = pd.DataFrame(columns=['node1', 'node2'], index=label_cols.index)
	    
	    for c in label_cols.index:
	        df_c = df_input.loc[df_input['label'] == c]
	        df_gini.loc[c, 'node1'] = df_c.loc[df_c['value']<= threshold_point].shape[0]
	        df_gini.loc[c, 'node2'] = df_c.loc[df_c['value']> threshold_point].shape[0]

        #计算node1、node2节点gini值中和的部分
	    sum_n1=df_gini['node1'].sum()
	    sum_n2=df_gini['node2'].sum()
	    # print(df_gini)

	    # 计算node节点gini值
	    gini_n1=gini_n2=0
	    if(sum_n1==0):
	    	for c in label_cols.index:
	    		gini_n2+=(df_gini.loc[c,'node2']/sum_n2)**2
	    elif(sum_n2==0):
	    	for c in label_cols.index:
	    		gini_n1+=(df_gini.loc[c,'node1']/sum_n1)**2
	    else:
	    	for c in label_cols.index:
		    	gini_n1+=(df_gini.loc[c,'node1']/sum_n1)**2
		    	gini_n2+=(df_gini.loc[c,'node2']/sum_n2)**2
	    gini_n1 = 1-gini_n1
	    gini_n2 = 1-gini_n2
	    #计算gini_split
	    gini_split=sum_n1/(sum_n1+sum_n2)*gini_n1 +sum_n2/(sum_n1+sum_n2)*gini_n2
	    # print("point:%f,gini_split:%f" %(threshold_point,gini_split))
	    return gini_split

	def get_best_point(self,value_series, label_series):
	    '''找到一列属性中最适合划分(gini值最小)的点'''

	    value_array=np.array(value_series)
	    value_array=np.sort(value_array)
	    df_point = pd.DataFrame(columns=['point', 'gini_value'])

	    # 循环属性值列,计算划分点及其gini值并添加至df_point数据表中
	    for i in range(len(value_array) + 1):
	        if(i == 0):
	            point = value_array[i] - 1
	        elif(i == len(value_array)):
	            point = value_array[i - 1]
	        else:
	            point = 0.5 * (value_array[i] + value_array[i - 1])
	        gini = self.get_col_gini(point, value_series, label_series)

	        s = pd.Series(data={'point': point, 'gini_value': gini})
	        df_point.loc[i] = s

	    df_point.sort_values(by='gini_value', inplace=True)
	    best_point = df_point.iloc[0, 0]
	    best_gini = df_point.iloc[0,1]
	    # print("best point for column '%s':%f" %(value_series.name,best_point))
	    # print(df_point)
	    return best_point,best_gini

	def get_best_column(self,data,label_series):
		'''遍历data中的属性列,计算其最佳划分点及gini值,找出最适合划分的一列和划分点'''
		x_data=data.copy()
		if(label_series.name in x_data.columns):
			del(x_data[label_series.name])

		gini_columns=pd.DataFrame(columns=['point','gini'],index=x_data.columns)
		for col_name in x_data.columns:
			point,gini=self.get_best_point(x_data[col_name],label_series)
			s=pd.Series({'point':point,'gini':gini})
			gini_columns.loc[col_name]=[point,gini]
			# gini_columns=gini_columns.append(s,ignore_index=True)	#append会更改索引
		gini_columns.sort_values(by='gini',inplace=True)
		# print(gini_columns)
		best_col=gini_columns.index[0]
		best_point=gini_columns.iloc[0,0]
		return best_col,best_point

	def get_ylabel(self,x_series):
		'''计算一行x数据(Series)对应的标签'''
		model=self.decision_df

		y_pred='#'
		x_index=1
		parent_index=[]
		child_df=pd.DataFrame()

		# for i in range(1):
		while (y_pred=='#'):
			#判断条件
			condition=[model.loc[x_index,'column'],model.loc[x_index,'point']]
			if(x_series[condition[0]]>condition[1]):
				x_index+=1
			# 	print('%s>%f' %(condition[0],condition[1]))
			# else:
			# 	print('%s<=%f' %(condition[0],condition[1]))
				
			y_pred=model.loc[x_index,'label']
			#更新父节点索引并找到其子节点
			parent_index=[model.loc[x_index,'this_depth'],model.loc[x_index,'this_pos']]
			child_df=model.loc[(model['parent_depth']==parent_index[0])&(model['parent_pos']==parent_index[1])]
			
			#找到标签时结束
			if(child_df.shape[0]!=0):
				x_index=list(child_df.index)[0]
			# 	print('跳到第%d行继续判断' %x_index)
			# else:
			# 	print('预测结束')
		# print('pred:',y_pred)
		return y_pred

	def show_tree(self):
		'''将决策树进行可视化'''

		def add_text(im_draw,text_str,xy,multiline=1):
			'''在绘图对象的某个位置添加文字'''
			#设置大小
			font_h,font_w=25,14
			font_h*=multiline
			text_len=round(len(text_str)/multiline)

			font=ImageFont.truetype(font='simsun.ttc',size=20)
			im_draw.text(xy=(xy[0]-font_w*3,xy[1]),text=text_str,font=font,fill='black',align='center')
			#绘制矩形
			# im_draw.rectangle(xy=(xy[0],xy[1],xy[0]+font_w*text_len,xy[1]+font_h),outline='black',width=2)

		interval_x,interval_y=60,80
		model=self.decision_df.copy()
		model['x_pos']=model['this_pos']
		model['y_pos']=(model['this_depth']-1)*interval_y
		model['text']='text'
		max_depth=model.iloc[-1,2]
		
		#创建图像
		img_w,img_h=1500,600
		tree_img=Image.new(mode='RGB',size=(img_w,img_h),color='white')
		draw=ImageDraw.Draw(tree_img) #创建绘图对象

		parent_pos=[]
		parent_x_pos=0
		x_pos=0
		for x_index in model.index:
			text=model.loc[x_index,'column']
			if (str(model.loc[x_index,'point']) == 'nan'):
				x_pos=img_w/4
			else:
				#跟新text内容和x位置
				model.loc[x_index,'x_pos']=x_pos
				parent_pos=[model.loc[x_index,'parent_depth'],model.loc[x_index,'parent_pos']]
				parent_x_pos=model.loc[(model['this_depth']==parent_pos[0])&(model['this_pos']==parent_pos[1]),'x_pos']
				depth=model.loc[x_index,'this_depth']-1
				if(model.loc[x_index,'this_pos']%2==0):
					text+='\n<='+('%.3f' %model.loc[x_index,'point'])
					x_pos=parent_x_pos-interval_x*np.sqrt(max_depth-depth)

				else:
					text+='\n>'+('%.3f' %model.loc[x_index,'point'])
					x_pos=parent_x_pos+interval_x*np.sqrt(max_depth-depth)
				x_pos=x_pos.iloc[0]
				if(model.loc[x_index,'label'] !='#'):
					text+='\nClass:'+str(model.loc[x_index,'label'])
			
			#将文字和位置添加到
			model.loc[x_index,'text']=text
			model.loc[x_index,'x_pos']=x_pos
		
		# 调整节点横坐标位置
		gap=140
		for depth in model['this_depth'].unique():

			if(depth!=1):
				same_depth=model.loc[model['this_depth']==depth]
				for x_index in same_depth.index[:-1]:
					if(x_index==same_depth.index[0]):
						if((model.loc[same_depth.index[0],'x_pos']-model.loc[same_depth.index[-1],'x_pos'])<gap*len(same_depth.index)):
							#如果整体太挤,整层先往左移一段
							for i in same_depth.index:
								model.loc[i,'x_pos']-=gap*len(same_depth.index)/8
					#如果相邻两个靠太近,右边的往右移一点
					if((model.loc[x_index+1,'x_pos']-model.loc[x_index,'x_pos'])<gap):
						model.loc[x_index+1,'x_pos']=model.loc[x_index,'x_pos']+gap
						# model.loc[x_index,'x_pos']-=gap/2

		#绘制文字和线
		this_img_pos=[]
		parent_img_pos=[]
		for x_index in model.index:
			#绘制直线
			if(x_index !=0):
				this_img_pos=[model.loc[x_index,'x_pos'],model.loc[x_index,'y_pos']]
				parent_pos=[model.loc[x_index,'parent_depth'],model.loc[x_index,'parent_pos']]
				parent_img_pos=model.loc[(model['this_depth']==parent_pos[0])&(model['this_pos']==parent_pos[1]),['x_pos','y_pos']]
				parent_img_pos=[parent_img_pos.iloc[0,0],parent_img_pos.iloc[0,1]]
				draw.line(xy=(parent_img_pos[0],parent_img_pos[1],this_img_pos[0],this_img_pos[1]),fill='gray',width=1)

			#添加文字
			this_pos=(model.loc[x_index,'x_pos'],model.loc[x_index,'y_pos'])
			text=model.loc[x_index,'text']
			add_text(im_draw=draw,text_str=text,xy=this_pos)
		
		#显示图片
		tree_img.show()


if __name__ == '__main__':

    # 读取文件
    data = pd.read_csv('./paras_labels.csv')
    #数据按8:2进行训练集、测试集切分
    x_train,x_test,y_train,y_test=train_test_split(data,data['label'],test_size=0.2,random_state=7)


    ds_tree=My_Decision_Tree()

    ds_tree.fit(x_train,y_train)
    # ds_tree.save_model('my_decision_tree%d.csv' %e)

    # ds_tree.load_model('my_decision_tree%d.csv' %e)
    # print(ds_tree.decision_df)

    print('训练集评估模型:')
    ds_tree.evaluate(x_train,y_train)
    print('测试集评估模型:')
    ds_tree.evaluate(x_test,y_test)

    ds_tree.show_tree()

结果:

用我的数据跑了一下,成功长出一棵歪脖子树,nice! 

python编程实现决策树算法python编程实现决策树算法

 代码能力有限,有错误的地方欢迎大佬们交流,批评指正[狗头抱拳]

 文章来源地址https://www.toymoban.com/news/detail-451274.html

 

 

到了这里,关于python编程实现决策树算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • python机器学习决策树和SVM向量机算法实现红酒分类

    经典的红酒分类数据集是指UCI机器学习库中的Wine数据集。该数据集包含178个样本,每个样本有13个特征,可以用于分类任务。 具体每个字段的含义如下: alcohol:酒精含量百分比 malic_acid:苹果酸含量(克/升) ash:灰分含量(克/升) alcalinity_of_ash:灰分碱度(以mEq/L为单位)

    2024年02月08日
    浏览(29)
  • Python实现决策树回归模型(DecisionTreeRegressor算法)并应用网格搜索算法调优项目实战

    说明:这是一个机器学习实战项目(附带 数据+代码+文档+视频讲解 ),如需 数据+代码+文档+视频讲解 可以直接到文章最后获取。 1.项目背景 决策树除了能进行分类分析,还能进行回归分析,即预测连续变量,此时的决策树称为回归决策树。回归问题是一类预测连续值的问

    2024年01月21日
    浏览(36)
  • 在西瓜数据集上用Python实现ID3决策树算法完整代码

    在西瓜数据集上用Python实现ID3决策树算法完整代码 1、决策树算法代码ID3.py 2、可视化决策树代码visual_decision_tree.py 3、贴几张运行结果图 1、生成的可视化决策树 2、代码运行结果 输出每次划分的每个属性特征的信息增益以及最后的决策树 3、记事本上手动跑程序的草图

    2024年02月08日
    浏览(36)
  • 分治法解二维的最近对问题,算法分析与代码实现,蛮力法与分治法解决二维的最近对问题的区别

    🎊【数据结构与算法】专题正在持续更新中,各种数据结构的创建原理与运用✨,经典算法的解析✨都在这儿,欢迎大家前往订阅本专题,获取更多详细信息哦🎏🎏🎏 🪔本系列专栏 -  数据结构与算法_勾栏听曲_0 🍻欢迎大家  🏹  点赞👍  评论📨  收藏⭐️ 📌个人主

    2024年02月04日
    浏览(29)
  • 使用Python中从头开始构建决策树算法

    决策树(Decision Tree)是一种常见的机器学习算法,被广泛应用于分类和回归任务中。并且再其之上的随机森林和提升树等算法一直是表格领域的最佳模型,所以本文将介绍理解其数学概念,并在Python中动手实现,这可以作为了解这类算法的基础知识。 在深入研究代码之前,

    2024年02月13日
    浏览(28)
  • 如何在Python中实现一个决策树算法?

    作者:禅与计算机程序设计艺术 在机器学习领域,决策树(decision tree)是一种常用的模式分类算法。它能够将输入数据划分成不同类别或不同输出值,并据此做出预测。而对于复杂的数据分析任务来说,用决策树这种经典算法进行分析就显得十分合适了。本文将通过对决策

    2024年02月08日
    浏览(41)
  • Python 机器学习入门 - - 决策树算法学习笔记

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 ChatGPT一问世就给整个社会带来巨大的震撼和冲击,不禁让人惊叹现在AI的强大,我们好像离通用人工智能更近一步。在过去十几年人工智能领域的蓬勃发展中,扮演着主导地位的算法基本都是神经网络和

    2023年04月08日
    浏览(31)
  • 基于机器学习的智能决策支持系统:实现智能化决策的关键算法

    作者:禅与计算机程序设计艺术 在现代社会,随着大数据、云计算、人工智能等新技术的发展,人们越来越重视决策效率与准确性。越来越多的人开始从事智能决策领域,通过数据分析预测未来的趋势,并根据此做出正确的决策。由于智能决策的本质就是进行一系列的决策规

    2024年02月07日
    浏览(35)
  • 数据挖掘|决策树算法以及相关算法实现和例题讲解

    声明:本专栏的所有内容皆是本人接触到的系统学习的老师的讲解内容,仅做整理分享。 出处来源:分类问题:决策树+ID3算法+C4.5算法+考试例题讲解_哔哩哔哩_bilibili 资料整理:链接:https://pan.baidu.com/s/1q786VaYJ9-1G7ZdfC6KL7A  提取码:3k7m

    2024年01月16日
    浏览(32)
  • 【毕业设计选题】基于深度学习的学生课堂行为检测算法系统 YOLO python 卷积神经网络 人工智能

    目录 前言 设计思路 一、课题背景与意义 二、算法理论原理 2.1 深度卷积神经网络 2.2 YOLOv5算法 三、检测的实现 3.1 数据集 3.2 实验环境搭建 3.3 实验及结果分析 实现效果图样例 最后        📅大四是整个大学期间最忙碌的时光,一边要忙着备考或实习为毕业后面临的就业升学

    2024年02月19日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包