特征的选择:标准:总方差最小;
回归树的生成:停止划分的标准;
剪枝。
选择标准:
遍历所有的特征Fi:遍历每个特征的所有特征值Zi;找到Zi,划分后总的方差最小
停止划分的条件:
当前数据集中的标签相同,返回当前的标签;
划分前后的总方差差距很小,数据不划分,返回的属性为空,返回的最佳划分值为当前所有标
签的均值;
划分后的左右两个数据集的样本数量较小,返回的属性为空,返回的最佳划分值为当前所有标
签的均值。
当划分的数据集满足上述条件之一,返回的最佳划分值作为叶子节点;
当划分后的数据集不满足上述要求时,找到最佳划分的属性,及最佳划分特征值。
# 计算总的方差
def GetAllVar(dataSet):
return var(dataSet[:,-1])*shape(dataSet)[0]
# 根据给定的特征、特征值划分数据集
def dataSplit(dataSet,feature,featNumber):
dataL = dataSet[nonzero(dataSet[:,feature] > featNumber)[0],:]
dataR = dataSet[nonzero(dataSet[:,feature] <= featNumber)[0],:]
return dataL,dataR
函数GetAllVar(dataSet)用于计算给定数据集的总方差。它首先使用var()函数计算数据集最后
一列的方差,然后将其乘以数据集的行数,得到总方差。
函数dataSplit(dataSet, feature, featNumber)用于根据给定的特征和特征值划分数据集。它接
受三个参数:dataSet表示要划分的数据集,feature表示要划分的特征的索引,featNumber表示特
征的划分值。
在函数内部,首先使用nonzero()函数找到数据集中满足特征大于给定特征值的行的索引,然
后使用这些索引从数据集中获取对应的行,得到划分后的左子集。同样的步骤也适用于特征小于等
于给定特征值的行,得到划分后的右子集。
nonzero()是一个函数,它的作用是返回一个数组或者列表中非零元素的索引。当我们需要找
到一个数组或者列表中非零元素的位置时,可以使用nonzero()函数来实现。这个函数会返回一个
包含非零元素索引的元组,其中第一个元素是非零元素的行索引,第二个元素是非零元素的列索引
(对于二维数组或者矩阵)。这样我们就可以通过这些索引来获取非零元素的具体位置。
# 特征划分
def choseBestFeature(dataSet,op = [1,4]): # 三个停止条件可否当作是三个预剪枝操作
if len(set(dataSet[:,-1].T.tolist()[0]))==1: # 停止条件 1
regLeaf = mean(dataSet[:,-1])
return None,regLeaf # 返回标签的均值作为叶子节点
Serror = GetAllVar(dataSet)
BestFeature = -1; BestNumber = 0; lowError = inf
m,n = shape(dataSet) # m 个样本, n -1 个特征
for i in range(n-1): # 遍历每一个特征值
for j in set(dataSet[:,i].T.tolist()[0]):
dataL,dataR = dataSplit(dataSet,i,j)
if shape(dataR)[0]
这段代码是一个特征划分函数,用于决策树算法中选择最佳特征进行数据划分。下面是对代码
的详细解释:
函数名:choseBestFeature
参数:op:一个包含两个元素的列表,用于设置停止条件。op[0]表示划分前后的误差异的最
小值,op[1]表示每个子集中样本的最小数量。
dataSet:数据集,包含特征和标签。
返回值:BestNumber:最佳特征的取值。BestFeature:最佳特征的索引。
停止条件:
条件1:如果数据集中的标签只有一种取值,即所有样本属于同一类别,则停止划分,将标签
的均值作为叶子节点的预测值。
条件2:如果划分前后的误差差异小于op[0],则停止划分,将整个数据集的标签均值作为叶子
节点的预测值。
条件3:如果划分后的子集中样本数量小于op[1],则停止划分,将子集的标签均值作为叶子节
点的预测值。
初始化变量:BestFeature:记录最佳特征的索引。BestNumber:记录最佳特征的取值。
lowError:记录最低误差差异。Serror:初始误差差异,即整个数据集的方差。
遍历每个特征:对于每个特征,遍历该特征的所有取值。将数据集根据当前特征和取值划分为
两个子集:dataL和dataR。
判断子集样本数量是否满足要求:如果dataL或dataR的样本数量小于op[1],则跳过当前特征
的该取值,继续下一次循环。
计算划分后的误差差异:使用GetAllVar函数计算dataL和dataR的方差之和,作为划分后的误
差差异。
更新最佳特征和最低误差差异:如果划分后的误差差异低于当前最低误差差异,则更新
BestFeature、BestNumber和lowError。
判断是否满足停止条件2:如果划分前后的误差差异小于op[0],则停止划分,将整个数据集的
标签均值作为叶子节点的预测值。
划分数据集:使用BestFeature和BestNumber将数据集划分为dataL和dataR。
判断子集样本数量是否满足要求:如果dataL或dataR的样本数量小于op[1],则停止划分,将
子集的标签均值作为叶子节点的预测值。
返回最佳特征和最佳特征取值。
# 决策树生成
def createTree(dataSet,op=[1,4]):
bestFeat,bestNumber = choseBestFeature(dataSet,op)
if bestFeat==None: return bestNumber
regTree = {}
regTree['spInd'] = bestFeat
regTree['spVal'] = bestNumber
dataL,dataR = dataSplit(dataSet,bestFeat,bestNumber)
regTree['left'] = createTree(dataL,op)
regTree['right'] = createTree(dataR,op)
return regTree
这段代码是用于生成决策树的函数。下面是对代码的详细解释:
createTree(dataSet,op=[1,4]):这是函数的定义,它接受两个参数。dataSet是一个数据集,
op是一个包含两个元素的列表,默认为[1,4]。
bestFeat,bestNumber = choseBestFeature(dataSet,op):调用choseBestFeature函数,该函
数用于选择最佳的特征和切分点。返回的bestFeat是最佳特征的索引,bestNumber是最佳切分点
的值。
if bestFeat==None: return bestNumber:如果bestFeat为None,即无法再选择最佳特征,那
么直接返回bestNumber作为叶子节点的值。
regTree = {}:创建一个空字典regTree,用于存储生成的决策树。
regTree['spInd'] = bestFeat:将最佳特征的索引存储在regTree字典中的spInd键下。
regTree['spVal'] = bestNumber:将最佳切分点的值存储在regTree字典中的spVal键下。
dataL,dataR = dataSplit(dataSet,bestFeat,bestNumber):调用dataSplit函数,该函数用于根
据最佳特征和切分点将数据集划分为左子集和右子集。返回的dataL是左子集,dataR是右子集。
egTree['left'] = createTree(dataL,op):递归调用createTree函数,生成左子树,并将其存储在
regTree字典中的left键下。
regTree['right'] = createTree(dataR,op):递归调用createTree函数,生成右子树,并将其存储
在regTree字典中的right键下。
return regTree:返回生成的决策树。
这段代码实现了一个递归的决策树生成算法。它通过选择最佳特征和切分点来构建决策树的节点,
并递归地生成左子树和右子树,直到无法再选择最佳特征为止。最终生成决策树以字典形式表示。
# 后剪枝操作
# 用于判断所给的节点是否是叶子节点
def isTree(Tree):
return (type(Tree).__name__=='dict' )
# 计算两个叶子节点的均值
def getMean(Tree):
if isTree(Tree['left']): Tree['left'] = getMean(Tree['left'])
if isTree(Tree['right']):Tree['right'] = getMean(Tree['right'])
return (Tree['left']+ Tree['right'])/2.0
# 后剪枝
def pruneTree(Tree,testData):
if shape(testData)[0]==0: return getMean(Tree)
if isTree(Tree['left'])or isTree(Tree['right']):
dataL,dataR = dataSplit(testData,Tree['spInd'],Tree['spVal'])
if isTree(Tree['left']):
Tree['left'] = pruneTree(Tree['left'],dataL)
if isTree(Tree['right']):
Tree['right'] = pruneTree(Tree['right'],dataR)
if not isTree(Tree['left']) and not isTree(Tree['right']):
dataL,dataR = dataSplit(testData,Tree['spInd'],Tree['spVal'])
errorNoMerge = sum(power(dataL[:,-1] - Tree['left'],2)) + sum(power(dataR[:,-1] - Tree['right'],2))
leafMean = getMean(Tree)
errorMerge = sum(power(testData[:,-1]- leafMean,2))
if errorNoMerge > errorMerge:
print("the leaf merge")
return leafMean
else:
return Tree
else:
return Tree
这段代码是一个用于后剪枝操作的函数。下面是对代码的详细解释:
isTree(Tree)函数用于判断给定的节点是否是叶子节点。它通过检查节点的类型是否为字典来
确定节点是否是叶子节点。
getMean(Tree)函数用于计算两个叶子节点的均值。如果节点的左子树或右子树是叶子节点,
则递归调用getMean()函数来计算子树的均值。最后,返回左右子树均值的平均值。
pruneTree(Tree, testData)函数是实际的后剪枝函数。它接受一个决策树节点和测试数据作为
输入。如果测试数据为空,则返回节点的均值。否则,如果节点的左子树或右子树是叶子节点,则
将测试数据根据节点的分割特征和分割值进行划分。
如果左子树是叶子节点,则递归调用pruneTree()函数对左子树进行后剪枝操作。
如果右子树是叶子节点,则递归调用pruneTree()函数对右子树进行后剪枝操作。如果左右子
树都不是叶子节点,则继续向下遍历决策树。
如果左右子树都是叶子节点,则将测试数据根据节点的分割特征和分割值进行划分,并计算不
合并叶子节点的误差和合并叶子节点的误差。
如果不合并叶子节点的误差大于合并叶子节点的误差,则将叶子节点合并,并返回合并后的叶
子节点的均值。
否则,返回原始的决策树节点。
总的来说,这段代码实现了一个后剪枝操作,用于对决策树进行修剪,以提高决策树的泛化能力。
# 预测
def forecastSample(Tree,testData):
if not isTree(Tree): return float(tree)
# print"选择的特征是:" ,Tree['spInd']
# print"测试数据的特征值是:" ,testData[Tree['spInd']]
if testData[0,Tree['spInd']]>Tree['spVal']:
if isTree(Tree['left']):
return forecastSample(Tree['left'],testData)
else:
return float(Tree['left'])
else:
if isTree(Tree['right']):
return forecastSample(Tree['right'],testData)
else:
return float(Tree['right'])
def TreeForecast(Tree,testData):
m = shape(testData)[0]
y_hat = mat(zeros((m,1)))
for i in range(m):
y_hat[i,0] = forecastSample(Tree,testData[i])
return y_hat
if __name__=="__main__":
print ("hello world")
from sklearn.model_selection import train_test_split
from sklearn.datasets import fetch_openml
data = fetch_openml(name='boston')
X_train, X_test, y_train, y_test = train_test_split(data.data, data.target,test_size=0.3)
这段代码是一个简单的决策树回归模型的预测函数。下面是对代码的详细解释:
forecastSample(Tree, testData)函数用于预测单个样本的目标值。它接受两个参数:Tree表示
决策树模型,testData表示待预测的样本数据。函数首先检查Tree是否为叶子节点,如果是,则返
回该叶子节点的值。否则,根据样本数据的特征值与决策树节点的划分值进行比较,选择相应的子
树进行递归预测,直到到达叶子节点为止。
TreeForecast(Tree, testData)函数用于预测整个测试集的目标值。它接受两个参数:Tree表示
决策树模型,testData表示待预测的测试集数据。函数首先获取测试集的样本数量m,然后创建一
个m×1的零矩阵y_hat用于存储预测结果。接下来,对于每个样本,调用forecastSample函数进行
预测,并将结果存储在y_hat中。最后,返回预测结果y_hat。
if __name__=="__main__":是Python中的特殊语法,表示当代码作为主程序运行时执行以下
代码块。在这里,它打印出"hello world"。
from sklearn.model_selection import train_test_split导入了train_test_split函数,用于将数据
集划分为训练集和测试集。
from sklearn.datasets import fetch_openml导入了fetch_openml函数,用于获取开放机器学习
数据集。
data = fetch_openml(name='boston')使用fetch_openml函数获取名为'boston'的数据集。这个
数据集是一个波士顿房价数据集,包含了506个样本和13个特征。
X_train, X_test, y_train, y_test = train_test_split(data.data, data.target,test_size=0.3)使用
train_test_split函数将数据集划分为训练集和测试集。data.data表示特征数据,data.target表示目
标数据,test_size=0.3表示将30%的数据作为测试集。
datatrain=np.concatenate((X_train,y_train[:,np.newaxis]),axis=1)
dataMat = mat(datatrain)
op = [1,6] # 参数1:剪枝前总方差与剪枝后总方差差值的最小值;参数2:将数据集划分为两个子数据集后,子数据集中的样本的最少数量;
theCreateTree = createTree(dataMat,op)
# 测试数据
datatest = np.concatenate((X_test, y_test[:, np.newaxis]), axis=1)
dataMat2=datatest[np.lexsort(datatest.T)]
dataMat2 = mat(dataMat2)
print(dataMat2.shape,y.shape)
datatrain=np.concatenate((X_train,y_train[:,np.newaxis]),axis=1)
dataMat = mat(datatrain)
op = [1,6] # 参数1:剪枝前总方差与剪枝后总方差差值的最小值;参数2:将数据集划分为两个子数据集后,子数据集中的样本的最少数量;
theCreateTree = createTree(dataMat,op)
# 测试数据
datatest = np.concatenate((X_test, y_test[:, np.newaxis]), axis=1)
dataMat2=datatest[np.lexsort(datatest.T)]
dataMat2 = mat(dataMat2)
#thePruneTree = pruneTree(theCreateTree, dataMat2)
#print"剪枝后的后树:\n",thePruneTree
y = dataMat2[:, -1]
y_hat = TreeForecast(theCreateTree,dataMat2)
plt.scatter(range(0, len(y)),y,c = 'r')
plt.scatter(range(0, len(y_hat)), y_hat,c = 'b')
plt.show()
这段代码是一个决策树的训练和测试过程。下面是对代码的详细解释:
datatrain=np.concatenate((X_train,y_train[:,np.newaxis]),axis=1): 将训练数据集X_train和
y_train合并为一个数组datatrain。其中,X_train是特征数据,y_train是目标变量。
dataMat = mat(datatrain): 将datatrain转换为矩阵形式,以便后续的决策树构建。
op = [1,6]: 定义了两个参数op,用于决策树的剪枝操作。参数1表示剪枝前后总方差差值的最
小值,参数2表示将数据集划分为两个子数据集后,子数据集中样本的最少数量。
theCreateTree = createTree(dataMat,op): 调用createTree函数,使用dataMat和op作为输
入,构建决策树theCreateTree。
datatest = np.concatenate((X_test, y_test[:, np.newaxis]), axis=1): 将测试数据集X_test和
y_test合并为一个数组datatest。其中,X_test是特征数据,y_test是目标变量。
dataMat2=datatest[np.lexsort(datatest.T)]: 对datatest进行排序,按照最后一列(目标变量)
进行排序。
dataMat2 = mat(dataMat2): 将dataMat2转换为矩阵形式,以便后续的决策树预测。
y = dataMat2[:, -1]: 将dataMat2的最后一列赋值给变量y,作为真实的目标变量。
y_hat = TreeForecast(theCreateTree,dataMat2): 调用TreeForecast函数,使用theCreateTree
和dataMat2作为输入,对测试数据进行决策树预测,得到预测结果y_hat。
plt.scatter(range(0, len(y)),y,c = 'r'): 使用散点图将真实的目标变量y以红色标记出来。
plt.scatter(range(0, len(y_hat)), y_hat,c = 'b'): 使用散点图将预测的目标变量y_hat以蓝色标记
出来。
plt.show(): 展示真实目标变量和预测目标变量之间的对比。