CART算法(Classification And Regression Tree,分类和回归树)是决策树的一种,由Leo Breiman, Jerome Friedman, Richard Olshen与Charles Stone于1984年提出,既可用于分类也可用于回归。本文将主要介绍用于分类的CART算法的R语言实现。
一、数据读入
数据来自Kaggle的cs-training.csv样本,该样本有15万条的个人消费类贷款数据。 数据下载地址
数据说明:变量名(原始)为文件中变量名,由于原始变量名较长,所以就转换成较短的y、x1、x2…代替。
变量名(原始) | 变量名 | 变量类型 | 变量描述 |
---|---|---|---|
id | integer | 序号 | |
SeriousDlqin2yrs | y | Y/N | 超过90天或更糟的逾期拖欠 |
RevolvingUtilizationOfUnsecuredLines | x1 | percentage | 无担保放款的循环利用:除了不动产和像车贷那样除以信用额度总和的无分期付款债务的信用卡和个人信用额度总额 |
age | x2 | integer | 借款人当时的年龄 |
NumberOfTime30-59DaysPastDueNotWorse | x3 | integer | 35-59天逾期但不糟糕次数 |
DebtRatio | x4 | percentage | 负债比率 |
MonthlyIncome | x5 | real | 月收入 |
NumberOfOpenCreditLinesAndLoans | x6 | integer | 开放式信贷和贷款数量,开放式贷款(分期付款如汽车贷款或抵押贷款)和信贷(如信用卡)的数量 |
NumberOfTimes90DaysLate | x7 | integer | 90天逾期次数:借款者有90天或更高逾期的次数 |
NumberRealEstateLoansOrLines | x8 | integer | 不动产贷款或额度数量:抵押贷款和不动产放款包括房屋净值信贷额度 |
NumberOfTime60-89DaysPastDueNotWorse | x9 | integer | 60-89天逾期但不糟糕次数:借款人在在过去两年内有60-89天逾期还款但不糟糕的次数 |
NumberOfDependents | x10 | integer | 家属数量:不包括本人在内的家属数量 |
#读入训练数据集
traindata<-read.csv("cs-training.csv",header = TRUE)
#原始变量名太长,重命名
names(traindata)=c("id","y","x1","x2","x3","x4","x5","x6","x7","x8","x9","x10")
#查看前6行数据,检查变量名是否更新完成
head(traindata)
#将因变量转变为因子格式
traindata$y=as.factor(traindata$y)
得到下面的结果,可以看到,变量名已经更新完成
> head(traindata)
id y x1 x2 x3 x4 x5 x6 x7 x8 x9 x10
1 1 1 0.7661266 45 2 0.80298213 9120 13 0 6 0 2
2 2 0 0.9571510 40 0 0.12187620 2600 4 0 0 0 1
3 3 0 0.6581801 38 1 0.08511338 3042 2 1 0 0 0
4 4 0 0.2338098 30 0 0.03604968 3300 5 0 0 0 0
5 5 0 0.9072394 49 1 0.02492570 63588 7 0 1 0 0
6 6 0 0.2131787 74 0 0.37560697 3500 3 0 1 0 1
二、建立训练集和测试集
select<-sample(1:nrow(traindata),length(traindata$id)*0.7) #随机选择70%的行数
train=traindata[select,]#随机选择70%的数据做训练集
test=traindata[-select,]#随机选择30%的数据做训练集
三、建立CART模型
(一)设置前剪枝的条件
tc<-rpart.control(minsplit = 50,minbucket = 20,maxdepth = 30,xval =10,cp = 0.001)
- minsplit 是最小分支节点数,这里指大于等于50,那么该节点会继续分划下去,否则停止
- minbucket 叶子节点最小样本数
- maxdepth 树的深度
- xval 是交叉验证的次数,xval=10就是10折交叉验证(将数据集分为10组,进行10次拟合,第i次拟合用除了第i组以外的数据训练,用第i组进行预测;目的是减少错误分类。
- cp 全称为complexity parameter,指某个点的复杂度,对每一步拆分,模型的拟合优度必须提高的程度,用来节省剪枝浪费的不必要的时间
(二)建立决策树
#搭建公式
formular=y~x1+x2+x3+x4+x5+x6+x7+x8+x9+x10
#建设模型,参数为举例说明,具体建模参数可以自行修改
rpart.mod=rpart(formular,data = train,method = "class",
parms = list(prior=c(0.6,0.4),loss=matrix(c(0,1,2,0),nrow=2),split="gini"),
control = tc)
- fomular 回归方程形式: 例如 y~ x 1+ x2+ x3。
- data 数据: 包含前面方程中变量的数据框( data frame) 。
- na.action 缺失数据的处理办法: 默认办法是删除因变量缺失的观测而保留自变量缺失的观测。
- method 根据树末端的数据类型选择相应因变量的分割方法,本参数有四种取值: 连续型>anova; 离散型>class; 计数型( 泊松过程)>poisson; 生存分析型>exp。程序会根据因变量的类型自动选择方法, 但一般情况下最好还是指明本参数, 以便让程序清楚做哪一种树模型。
- parms 用来设置三个参数:先验概率、损失矩阵、分类纯度的度量方法。anova没有参数;poisson分割有一个参数,先验分布变异系数的比率,默认为1;生存分布的参数和poisson一致;对离散型,可以设置先验分布的分布的概率(prior),损失矩阵(loss),分类纯度(split);
- priors必须为正值且和为1
- loss为代价矩阵,必须对角为0且非对角为正数,代价矩阵的行代表实际的分类,列代表预测的分类,在剪枝的时候,叶子节点的加权误差与父节点的误差进行比较,考虑损失矩阵的时候,从将“减少-误差”调整为“减少-损失”
- split可以是gini(基尼系数)或者information(信息增益);
- control 控制每个节点上的最小样本量、交叉验证的次数、复杂性参量: 即cp: complexity pamemeter, 这个参数意味着对每一步拆分, 模型的拟合优度必须提高的程度, 等等
(二)后剪枝过程
在分类回归树中可以使用的后剪枝方法有很多,比如:代价复杂度剪枝、最小误差剪枝、悲观误差剪枝等等,这里我们只介绍代价复杂度剪枝法。
rpart包提供了复杂度损失修剪的修剪方法,rpart.mod$cp会告诉分裂到每一层,cp(复杂度)是多少,平均相对误差是多少;交叉验证的估计误差(xerror)、标准误差(xstd)、平均相对误差(xerror±xstd);也可以通过plotcp把cp的折线图打印出来。
> rpart.mod$cp
CP nsplit rel error xerror xstd
1 0.454950358 0 1.0000000 1.0247830 0.009133535
2 0.040251306 1 0.5450496 0.5487305 0.006440166
3 0.019101365 2 0.5047983 0.5085355 0.005126169
4 0.006685254 3 0.4856970 0.4898545 0.004781973
5 0.004512247 4 0.4790117 0.4738762 0.004770271
6 0.003859716 10 0.4484181 0.4541848 0.005548255
7 0.001652621 13 0.4368389 0.4427585 0.005900042
8 0.001419401 15 0.4335337 0.4410733 0.005862466
9 0.001371489 16 0.4321143 0.4391973 0.005865430
10 0.001242517 19 0.4279998 0.4397694 0.005899587
11 0.001201155 21 0.4255148 0.4387756 0.005886477
12 0.001048985 22 0.4243136 0.4368511 0.005834060
13 0.001019105 23 0.4232646 0.4368356 0.005817677
14 0.001000000 24 0.4222455 0.4367234 0.005817659
> plotcp(rpart.mod)
复杂度剪枝法满足的条件是,在交叉验证的估计误差(xerror)尽量小的情况下(不一定是最小值,而是允许最小误差的一个标准差(xstd)之内),选择尽量大的cp值。我们这里选择cp=0.001652621。
> rpart.mod.pru<-prune(rpart.mod,cp=0.001652621)
> rpart.mod.pru$cp
CP nsplit rel error xerror xstd
1 0.454950358 0 1.0000000 1.0247830 0.009133535
2 0.040251306 1 0.5450496 0.5487305 0.006440166
3 0.019101365 2 0.5047983 0.5085355 0.005126169
4 0.006685254 3 0.4856970 0.4898545 0.004781973
5 0.004512247 4 0.4790117 0.4738762 0.004770271
6 0.003859716 10 0.4484181 0.4541848 0.005548255
7 0.001652621 13 0.4368389 0.4427585 0.005900042
8 0.001652621 15 0.4335337 0.4410733 0.005862466
当然,我们也可以选择具有最小xerror的cp的方法,代码如下:
rpart.mod.pru<-prune(rpart.mod,cp=rpart.mod$cptable[which.min(rpart.mod$cptable[,"xerror"])])
四、画出最终的决策树
方法一:使用rpart.plot()
rpart.plot(rpart.mod.pru,branch=1,extra = 102,under = TRUE,faclen = 0,cex = 0.7,main="CART")
方法二:使用plot()和text()
plot(rpart.mod.pru)
text(rpart.mod.pru,all = TRUE,digits = 7,use.n = TRUE,cex=0.9,xpd=TRUE)
方法三:使用draw.tree()
draw.tree(rpart.mod.pru)
五、模型评估
(一)参数的重要性
> rpart.mod.pru$variable.importance
x1 x3 x7 x9 x6 x2 x8 x4
11071.77272 4437.97071 4318.85268 1829.67024 898.98357 227.93847 35.51209 11.18615
(二)ROC曲线
test.pre<-predict(rpart.mod.pru,test)
plot(roc(test$y,test.pre[,2]), print.auc=TRUE, auc.polygon=TRUE, grid=c(0.1, 0.2),
grid.col=c("green", "red"), max.auc.polygon=TRUE,
auc.polygon.col="skyblue", print.thres=TRUE)
(三)混淆矩阵
> test$pre_p<-predict(rpart.mod.pru,test)[,2]
> test$pre=0
> test$pre[which(test$pre_p>0.431)]=1
> confusionMatrix(test$pre,test$y,positive='1')
Confusion Matrix and Statistics
Reference
Prediction 0 1
0 32997 703
1 8953 2347
Accuracy : 0.7854
95% CI : (0.7816, 0.7892)
No Information Rate : 0.9322
P-Value [Acc > NIR] : 1
Kappa : 0.2467
Mcnemar's Test P-Value : <2e-16
Sensitivity : 0.76951
Specificity : 0.78658
Pos Pred Value : 0.20770
Neg Pred Value : 0.97914
Prevalence : 0.06778
Detection Rate : 0.05216
Detection Prevalence : 0.25111
Balanced Accuracy : 0.77804
'Positive' Class : 1
版权声明:本文为大枫原创文章,转载请注明来源。