R语言笔记之树模型(回归树和决策树)
阅读原文时间:2021年04月21日阅读:1

回归树过程:

例:有10名学生,他们的身高分布如下:
R1:
女生(7):156,167,165,163,160,170,160
R2:
男生(3):172,180,176
那么,落入R1的样本均值为163,落入R2的样本均值为176,那么对于新样本,如果是女生,树模型预测的身高是163,是男生,则为176.
那么如何划分出区域R1,R2(建造树模型)?
需要使用自上到下的贪婪算法—–递归二元分割,即从根节点逐步向下分隔,每次产生两个树枝(二元分割)
R中可以建造回归树的包:ctree,rpart,tree

> library(rpart)
> library(tree)
Error in library(tree) : 不存在叫‘tree’这个名字的程辑包
> install.packages("tree")
trying URL 'https://cran.rstudio.com/bin/windows/contrib/3.4/tree_1.0-37.zip'
Content type 'application/zip' length 122090 bytes (119 KB)
downloaded 119 KB

package ‘tree’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\LLJiang\AppData\Local\Temp\RtmpmMgvpx\downloaded_packages
> library(tree)
Warning message:
程辑包‘tree’是用R版本3.4.3 来建造的 
> dat=read.csv("https://raw.githubusercontent.com/happyrabbit/DataScientistR/master/Data/SegData.csv")
> dat=subset(dat,store_exp>0&online_exp>0)
> trainx=dat[,grep("Q",names(dat))]
> trainy=dat$store_exp+dat$online_exp
> set.seed(100)
> rpartTrue=train(trainx,trainy,method="rpart2",tuneLength=10,trControl=trainControl(method="cv"))
> plot(rpartTrue)
> 


如上图,树的最大深度大于2,RMSE就不再变化了,这里我们就用深度2来建立树

> rpartTrue=rpart(trainy~.,data=trainx,maxdepth=2)
> print(rpartTrue)
n= 999 

node), split, n, deviance, yval
      * denotes terminal node

1) root 999 15812720000  3479.113  
  2) Q3< 3.5 799  2373688000  1818.720  
    4) Q5< 1.5 250     3534392   705.193 *
    5) Q5>=1.5 549  1919009000  2325.791 *
  3) Q3>=3.5 200  2436211000 10112.380 *
> 

Q3,Q5被最终预测总花销
以下为对rpart生成的树绘制图形

> library(partykit)
载入需要的程辑包:grid
载入需要的程辑包:libcoin
载入需要的程辑包:mvtnorm
Warning messages:
1: 程辑包‘partykit’是用R版本3.4.3 来建造的 
2: 程辑包‘libcoin’是用R版本3.4.3 来建造的 
> rpartTrue2=as.party(rpartTrue)
> plot(rpartTrue2)
> 

决策树

其目标是把数据划分为更小,同质性更强的组
与回归树不同在于,因变量是分类变量而不是数值。
故预测并不是基于平均而是基于每个类别样本的频数。叶节点的预测值就是落入相应区域训练集样本中频数最高的类别。
其分裂准则不是RSS,而是熵或者Gini系数
当自变量是连续型时,确定最佳分裂点的划分过程很之间,然而当自变量是分类型时,有两种处理方式
1.不对分类变量进行变换,每个分类型自变量作为单独的个体输入到模型中。
2.分类型自变量先被重新编码为二元虚拟变量,这样讲类别信息分解成独立信息块。
如果某些类对结果又强预测性,第一种方法更合适。
下面我们用不同方法对服装消费者性别进行判定(分类模型)

> library(pROC)
Type 'citation("pROC")' for a citation.

载入程辑包:‘pROC’

The following objects are masked from ‘package:stats’:

    cov, smooth, var

Warning message:
程辑包‘pROC’是用R版本3.4.3 来建造的 
> dat=read.csv("https://raw.githubusercontent.com/happyrabbit/DataScientistR/master/Data/SegData.csv")
#将10个问卷调查变量当做自变量

> trainx1=dat[,grep("Q",names(dat))]
#将类别也作为自变量
#用两种方法编码分类变量
#trainx1不对消费者类别进行变换

> trainx1$segment=dat$segment
 #trainx2中消费者类别被转换成虚拟变量

> dumMod=dummyVars(~.,data=trainx1,levelsOnly=F)
#用原变量名加上因子层级的名称作为新的名义变量名
> trainx2=predict(dumMod,trainx1)
#性别作为因变量
> trainy=dat$gender

不对分类变量进行编码,cp指复杂度参数,是树生长的停止准则,cp=0.01意味着相应分裂度量(熵,Gini)每一步分裂都需要比之前提高0.01,在交互校检结果中不满0.01提升的部分会被修剪掉

> set.seed(100)
> rpartTune1=caret::train(trainx1,trainy,method="rpart",tuneLength=30,metric="ROC",trControl=trainControl(method="cv",summaryFunction=twoClassSummary,classProbs=TRUE,savePredictions=TRUE))
> rpartTune1
CART 

1000 samples
  11 predictor
   2 classes: 'Female', 'Male' 

No pre-processing
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 901, 899, 900, 900, 901, 900, ... 
Resampling results across tuning parameters:

  cp           ROC        Sens       Spec     
  0.000000000  0.6936668  0.6516883  0.6883838
  0.008350085  0.7026106  0.6118506  0.7354545
  0.016700170  0.6851629  0.5324351  0.8204545
  0.025050255  0.6802976  0.5107468  0.8498485
  0.033400340  0.6802976  0.5107468  0.8498485
  0.041750425  0.6802976  0.5107468  0.8498485
  0.050100510  0.6802976  0.5107468  0.8498485
  0.058450595  0.6802976  0.5107468  0.8498485
  0.066800680  0.6802976  0.5107468  0.8498485
  0.075150765  0.6802976  0.5107468  0.8498485
  0.083500850  0.6802976  0.5107468  0.8498485
  0.091850936  0.6802976  0.5107468  0.8498485
  0.100201021  0.6802976  0.5107468  0.8498485
  0.108551106  0.6802976  0.5107468  0.8498485
  0.116901191  0.6802976  0.5107468  0.8498485
  0.125251276  0.6802976  0.5107468  0.8498485
  0.133601361  0.6802976  0.5107468  0.8498485
  0.141951446  0.6802976  0.5107468  0.8498485
  0.150301531  0.6802976  0.5107468  0.8498485
  0.158651616  0.6802976  0.5107468  0.8498485
  0.167001701  0.6802976  0.5107468  0.8498485
  0.175351786  0.6802976  0.5107468  0.8498485
  0.183701871  0.6802976  0.5107468  0.8498485
  0.192051956  0.6802976  0.5107468  0.8498485
  0.200402041  0.6802976  0.5107468  0.8498485
  0.208752126  0.6802976  0.5107468  0.8498485
  0.217102211  0.6802976  0.5107468  0.8498485
  0.225452296  0.6802976  0.5107468  0.8498485
  0.233802381  0.6340747  0.5936039  0.6745455
  0.242152466  0.5556313  0.7872727  0.3239899

ROC was used to select the optimal model using the largest value.
The final value used for the model was cp = 0.008350085.
> 

对将分类变量进行编码后的数据集进行训练

> rpartTune2=caret::train(trainx2,trainy,method="rpart",tuneLength=30,metric="ROC",trControl=trainControl(method="cv",summaryFunction=twoClassSummary,classProbs=TRUE,savePredictions=TRUE))
> rpartRoc=roc(response=rpartTune1$pred$obs,predictor=rpartTune1$pred$Female,levels=rev(levels(rpartTune$pred$obs)))
Error in levels(rpartTune$pred$obs) : object 'rpartTune' not found
> rpartRoc=roc(response=rpartTune1$pred$obs,predictor=rpartTune1$pred$Female,levels=rev(levels(rpartTune1$pred$obs)))
> rpartFactorRoc=roc(response=rpartTune2$pred$obs,predictor=rpartTune2$pred$Female,levels=rev(levels(rpartTune1$pred$obs)))
> plot(rpartRoc,type="s",print.thres=c(.5),print.thres.pch=3,print.thres.pattern="",print.thres.cex=1.2,col="red",legacy.axes=TRUE,print.thres.col="red")

> plot(rpartFactorRoc,type="s",add=TRUE,print.thres=c(.5),print.thres.pch=16,legacy.axes=TRUE,print.thres.pattern="",print.thres.cex=1.2)

> legend(.75,.2,c("Grouped Categories","Independent Categories"),lwd=c(1,1),col=c("black","red"),pch=c(16,3))
> 


由上图可以看出,对于使用CART构建的树,对消费者类别变量编码或者不编码并不影响对受访者性别做预测
下面我们通过partykit包对最终模型绘制图形。

> library(partykit)
> plot(as.party(rpartTune2$finalModel))
> 

单棵树很直观,容易解释,但它有两个缺点:
1.和很多回归模型相比精确度差
2.非常不稳定,数据微小的变化会导致模型结果很大的变化。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器