导入包
import numpy as np
import pandas as pd
定义一个数据集
def create_data():
datasets = [['青年', '否', '否', '一般', '否'],
['青年', '否', '否', '好', '否'],
['青年', '是', '否', '好', '是'],
['青年', '是', '是', '一般', '是'],
['青年', '否', '否', '一般', '否'],
['中年', '否', '否', '一般', '否'],
['中年', '否', '否', '好', '否'],
['中年', '是', '是', '好', '是'],
['中年', '否', '是', '非常好', '是'],
['中年', '否', '是', '非常好', '是'],
['老年', '否', '是', '非常好', '是'],
['老年', '否', '是', '好', '是'],
['老年', '是', '否', '好', '是'],
['老年', '是', '否', '非常好', '是'],
['老年', '否', '否', '一般', '否'],
]
labels = [u'年龄', u'有工作', u'有自己的房子', u'信贷情况', u'类别']
#u: 表示unicode字符串,默认模式,里边的特殊字符会被识别。比如\t会识别为空格
# 返回数据集和每个维度的名称
return datasets, labels
datasets,labels=create_data()
train_data=pd.DataFrame(datasets,columns=labels)
定义Node,在下面调用时传回一个实例化对象。
#定义节点类,二叉树
class Node:
def __init__(self,root=True,label=None,feature_name=None,feature=None):
self.root = root
self.label = label
self.feature_name = feature_name
self.feature = feature
self.tree={}
self.result = {'label:':self.label,'feature':self.feature,'tree':self.tree}
def __repr__(self):
return'{}'.format(self.result)
#它是一个 ”自我描述“ 的方法,此方法通常实现这样的功能: 当直接打印类的实例化对象时,系统将会输出对象的自我描述信息,用来告诉外界对象具有的状态信息。
def add_node(self,val,node):
self.tree[val]=node
def predict(self,features):
if self.root is True:
return self.label
return self.tree[features[self.feature]].predict(features)
定义类DTree
class DTree:
def __init__(self,epsilon=0.1):
self.epsilon = epsilon
self._tree = {}
# 通过@staticmethod声明的,属于类的静态方法
#对于类中的普通函数,当需要调用的时候必须实例化,对于声明为staticmethod/classmethod ,不需要实例化,直接使用类名调用。
#熵
@staticmethod
def calc_ent(datasets):
data_length =len(datasets)
label_count={}
for i in range(data_length):
label = datasets[i][-1]
if label not in label_count:
label_count[label] = 0
label_count[label] += 1
ent = -sum([(p/data_length)*log(p/data_length,2) for p in label_count.values()])
return ent
#经验条件熵
def cond_ent(self, datasets, axis=0):
data_length = len(datasets)
feature_sets = {}
for i in range(data_length):
feature = datasets[i][axis]
if feature not in feature_sets:
feature_sets[feature] = []
feature_sets[feature].append(datasets[i])
cond_ent = sum([(len(p)/data_length)*self.calc_ent(p) for p in feature_sets.values()])
return cond_ent
#信息增益
@staticmethod
def info_gain(ent, cond_ent):
return ent - cond_ent
def info_gain_train(self, datasets):
count = len(datasets[0]) - 1
ent = self.calc_ent(datasets)
best_feature = []
for c in range(count):
c_info_gain = self.info_gain(ent, self.cond_ent(datasets, axis=c))
best_feature.append((c, c_info_gain))
# 比较大小
best_ = max(best_feature, key=lambda x: x[-1]) #key定义了比较大小的位置,key还可以是某种运算函数,使最大值是使用函数后得到的
return best_ #返回最大的索引和它对应的信息增益
def train(self,train_data):
"""
input:数据集D,特征集A,阈值eta
output:决策树T
"""
_,y_train,features = train_data.iloc[:,:-1],train_data.iloc[:,-1],train_data.columns[:-1] #y_train:是否贷款,features:列名
#若D中实例属于同一类ck,则T为单节点树,并将类ck作为节点的类标记,返回T
if len(y_train.value_counts())==1:
return Node(root=Ture,label=y_train.iloc[0])#因为都是一类的,所以取第一个
#若A为空,则T为单节点树,将D中实例树最大的类Ck作为该节点的类标记,返回T
if len(features) == 0:
return Node(root=True, label=y_train.value_counts().sort_values(ascending=False).index[0])
#计算最大信息增益 同5.1,Ag为信息增益最大的特征
max_feature, max_info_gain = self.info_gain_train(np.array(train_data))
max_feature_name = features[max_feature]
# Ag的信息增益小于阈值eta,则置T为单节点树,并将D中是实例数最大的类Ck作为该节点的类标记,返回T
if max_info_gain < self.epsilon:
return Node(root=True, label=y_train.value_counts().sort_values(ascending=False).index[0])
# 构建Ag子集
node_tree = Node(root=False, feature_name=max_feature_name, feature=max_feature)
feature_list = train_data[max_feature_name].value_counts().index #max_feature_name那一列有几类
for f in feature_list:
sub_train_df = train_data.loc[train_data[max_feature_name] == f].drop([max_feature_name], axis=1) #比如把所有年龄是青年的数据提取出来后,再把年龄这列数据删了
# 6, 递归生成树
sub_tree = self.train(sub_train_df)
node_tree.add_node(f, sub_tree)
# pprint.pprint(node_tree.tree)
return node_tree
def fit(self, train_data):
self._tree = self.train(train_data)
return self._tree
def predict(self, X_test):
return self._tree.predict(X_test)
使用数据集将其实现
datasets, labels = create_data()
data_df = pd.DataFrame(datasets, columns=labels)
dt = DTree() #类实例化
tree = dt.fit(data_df)