/ ~% M H4 e$ I1 C* ^决策树(Decision Tree):Tree-Based方法用于多元数据的分类和回归。决策树点是再现了人类做决策的过程,树可以图形化显示,很容易解释。但是树的分类和回归准确度比不上其他分类和绘图模型。决策树是随机森林、boosting等组合方法的基本组件,组合大量的树通常会显著提高模型的预测准确度,但会损失一些解释性。定性与定量变量均可用于Tree-Based方法。tree\rpart\mvpart均可进行多元树分析及绘图。此文介绍如何使用tree包进行多元分类/回归决策树分析。+ H# R% ?& K. o7 ^' l& u
Y0 M5 d' T! j+ f 一、 准备数据 % X4 H& }, ]) u3 ^9 D' |6 y 6 Z; c% J m$ X# a( x, \6 y# d% r+ h虚构微生物组和环境因子数据,包含75个样本。1 E7 D, ~; o# `/ f; `
" ^ ?8 ^( v- ^7 g- A+ e
# 1.1 导入数据; I" O7 Z# ^- g0 M! B# S
## 微生物组数据 9 h" C7 A/ x. B# @3 s1 Tspe = read.csv("spe.csv",row.names = 1,header = TRUE,check.names = TRUE,stringsAsFactors = TRUE) 3 n8 m( Q0 e }1 t8 N
dim(spe) 9 ]5 u: ?( Q1 k( W" j% l8 _ v0 {head(spe)& b: m# K# d Y. l3 g* p
5 r1 F0 K* Q! e6 T9 Z! [! h0 X7 G
## 环境因子数据/ X1 g6 P2 P! Z# z* F7 C* z
env = read.csv("env.csv",row.names = 1,header = TRUE,check.names = TRUE,stringsAsFactors = TRUE) , _: F2 r; s$ C* U4 S) qdim(env) ; ]. @& i( v0 P1 o' vhead(env)+ y1 k# |- M$ M6 t4 A' u/ w3 K
7 J" b; U& Q' p" _" q
: }* R( O# z! p0 E* R. a3 W
图1|原始otu表,spe.csv。前两列为分类信息。 3 m. ^: W3 T0 I7 g; I: H$ Q1 B- t5 [( x* G. `6 r3 O. I
& c* _" m5 I6 b8 b' |9 D0 a, Z
, l/ o( G. \: K- D, W
图2|环境因子数据,env.csv。% \0 Y4 P2 X; z0 l+ |
. k" ]" l3 j! [8 G3 E* d+ i
二、决策树回归模型 & u9 \: A/ I2 `9 D当因变量为定量变量时,决策树进行回归分析。分析时先基于自变量划分预测空间Rj,此时残差平方和(residual sum of squares,RSS)值最小,然后对于落入某一预测空间的样本,做相同的预测。 $ Q, Y$ {$ E3 K- k0 g+ H& W1 x% U & v. u1 ^5 F1 R1 P3 y0 d9 ` ( v8 y: z! e. t# x3 i : h* W( i p6 `4 E+ J但是考虑到所有自变量的预测空间构建基本很难实现,因此常使用递归二进制拆分(recursive binary splitting)。每一步的拆分都使RSS减少量最大。 - g! z$ i% G! S: E. A- R' d }' V* B0 w8 L4 f& n0 ]$ v/ f6 i
如拆分点(cutpoint)为s,则拆分空间R1和R2为:# M# K5 V& } [1 Q5 k
) F7 r( d2 L& D1 s* y; d
* Q- t( e" h5 a r: Q5 T' u* Z9 E
! i# |& `' Q G3 Z, T
j和s的选择基于使RSS最小化:5 S. S% b0 a* }2 c2 z
. i* l [* J; h( ^' J( K0 W 7 f+ k; [1 f% O% y, u$ H 9 z- o0 X5 U6 h9 k- ~ L. g" M% c后面重复此过程,寻找最佳预测空间和拆分点,从而使每个结果区域的RSS值最小,直到达到终止拆分标准,比如每个终端节点包含的样本数都不高于设定的阈值。 - o: Q6 a" @4 s5 e9 l9 S3 _- k: a8 y
2.1 构建回归决策树 . `" B0 f( A q" T使用微生物数据与环境因子数据进行决策树回归分析。为了更好的评估分类树的分类性能,不能只计算训练误差,需要估计测试误差。将数据分为训练集和测试集数据,训练集数据用于构建模型,测试集数据用于模型评估。 3 f% ?6 a5 s( `6 X 7 M v n3 h/ `2 E5 |6 }# 2.1.1 将数据集分为train和test集,用train结果预测test的因变量值。 # h3 M$ A7 [' }4 o$ V$ Ilibrary(splitstackshape)& ~+ v) t4 e" h9 ~
spe = data.frame(ID = rownames(spe),spe)# stratified提取后,样本名会消失,先提取样本名,重新构建数据框。- F9 w0 q% b& l2 Z- [) N1 h+ O
9 [. z- q0 n$ C! T: u' o O## train data sets,每个分类提取相同数目的样本用作训练集 7 q. L; E x2 z& ]0 Gset.seed(12345)* L' ~, @: m! T+ Q4 r
train.spe = stratified(spe, group=c("grazing"),size=10,replace=FALSE) 0 b7 }5 Z4 f- a! A& H3 T2 ~& T2 \table(train.spe$grazing) # 每个分类提取的样本数一致。- ?$ v2 x4 ~6 ^" _; g- U
1 y, C( ^! f8 a' l% Ytrain.env = env[rownames(env) %in% train.spe$ID,] 2 T4 l$ K. z. U3 m2 l0 ytable(train.env$grazing) # 每个分类提取的样本数一致。 % Z; P0 C3 T3 X% z0 {& V9 Z" r" T- N7 v% C3 S. d; ~
## test data sets J. {) a8 W- |3 ]test.spe = spe[!spe$ID %in% train.spe$ID,] , b3 j; g/ S& W4 i: h& ztable(test.spe$grazing) # Z7 l/ x m1 Q* C& S% A! f" O5 D# K+ {
test.env = env[!rownames(env) %in% rownames(train.env),] 5 ~6 L% H0 ^5 K5 \0 H1 Ktable(test.env$grazing)6 O$ M( G! u7 o7 D
+ \+ {* J _! K
#install.packages("tree")2 N. a% w8 t/ S7 {3 `
library(tree) ( L. Z2 A3 _" [" m. a3 F) d p: E5 o2 m, ~( U& F
# 2.1.2 构建回归决策树 * U7 l, z% H& W! f3 d5 zreg.tre = tree::tree(train.env$env1 ~.,data=train.spe[,-c(1:3)]) 2 E9 N, ~+ m) z( \- ireg.tre A9 a7 f9 @/ I' g3 b
; I: h; A: L h
# 2.1.3 输出结果简介 6 c1 i( J5 H& e' f/ m" G7 u## 输出表格的行为节点名(整数值表示),包含9列数据。& x B: W" u, K
reg.tre$frame $ o7 `, R7 |2 H- h) Q## 列包括var:用于拆分节点的变量及终端节点(<leaf>); k8 G a8 s) P, k: U0 e o: _reg.tre$frame$var2 ?( a8 Q) {7 Q" D y; [7 i
## n:每个节点的样本数量; E" Y( R, Q8 w! \ s& }3 B
reg.tre$frame$n + S7 f! |% S, S' d) [2 [## dev:每个节点的偏差7 ]5 z7 v; {! \2 }8 l: w4 X$ e
reg.tre$frame$dev 7 u& [- B4 x& K" a( b## yval:拟合结果,回归树为节点包含样本的因变量均值,分类树为该节点样本最多属于的分类水平; " ~5 N+ Q) P$ G4 Z#mean(train.env[reg.tre$where == 4,3]) # 第四个节点包含样本的因变量均值。! U7 Y/ j1 g% |1 y7 v& s
reg.tre$frame$yval 1 H3 J3 l' l# G$ \, z- |& H% l3 t 3 K; s- L( m9 J+ N @ n## split: 节点拆分,2列分别是属于左侧或右侧的标签;- R' X z9 L# z! X$ {* R- ]
reg.tre$frame$splits / v& K. e$ n z; s* _: u## yprob:回归树,此为NULL;分类树则为因变量各水平的拟合比率,此数据有5个处理,所以有5列。3 U! S! m/ x0 u, R$ _
reg.tre$frame$yprob ' g5 \! `) z W _9 r9 Z0 w. L [# i
## output,需要输出行名,则设置row.names=TRUE。 2 f2 w5 g6 N0 `/ b' Hwrite.table(reg.tre$frame,"reg_tre_res.txt",sep="\t",quote = FALSE,row.names = FALSE)) v K5 q! T, T5 `* P7 y) G. R2 r/ b
) m: V4 R$ C% w ^7 | ?$ E
## 每个样本所属节点# m8 D1 x$ R9 G m; t
reg.tre$where ( S1 Z! W3 Z" p2 L- h## formul形式4 W' m' j& F3 o/ o9 _! K
reg.tre$terms / n) t1 ?6 ?6 Y# K## 自变量数据,x=FALSE则不会返回此数据. _7 T* O1 L" K$ I: j
reg.tre$x% a- z' ~0 U0 g | \
## 因变量,y=FALSE则不会返回此数据7 M6 L" z% |& v( G7 _ O* o
reg.tre$y0 {: S- i3 S0 ~+ e6 q6 `4 R
## 样本权重,未设置则均为1,权重值可以为分数形式。2 ^9 A0 f0 m7 [8 S$ E
reg.tre$weights ) M& l- _1 ^ _1 `* u7 N$ U; z1 C% Y# ]
## 结果描述统计& |* h9 [' V, L; ]$ v6 i
reg.tre.res = summary(reg.tre) 9 Y% [ J4 }2 W5 ?1 ^( O3 oreg.tre.res0 L# M2 R$ ~3 I2 ~$ S8 n% t- M
reg.tre.res$used # 用于构建回归决策树的自变量 ) l' ?; p+ Z( A6 n/ ]reg.tre.res$dev # 偏差,决策树的残差平方和。: H* w3 a ^# l% A& t. m
reg.tre.res$df # 训练样本数减去终端节点数, [: C) U* V% ?- ^( ]
reg.tre.res$residuals# 每个训练样本因变量的残差" l6 y4 W% B1 W' S6 {
) h$ V6 }7 h% q y' `6 a## 简单绘图 * G6 f8 ?, f. ~2 P# ^0 cplot(reg.tre)8 Q7 m9 |% H8 ~% s3 `/ _
text(reg.tre,pretty = 0) 8 n. o! ?9 _2 H1 J7 {3 O6 ?& ^$ h( ]8 G, s
# 2.1.4 预测测试集数据 v F0 P# G9 N# W1 U; f* Qreg.pred = predict(reg.tre,newdata = test.spe[,-c(1:3)])& @ X4 y/ i0 h4 \4 T6 e
reg.pred # h) c! R# |# S/ U9 y
## 预测结果与原始结果绘图 % M6 `& l& e8 j4 Z5 L+ B, y' Z, oplot(reg.pred,test.env$env1) 8 ]. }+ E% O; k+ t( Q% Y7 Rabline(0,1)$ N8 ]* V) E. q: J" O
$ h3 n7 ^! N, R* b6 S
## 计算残差平方和(MSE)和标准化均方误差(NMSE) / G$ i& T2 K6 X6 wMSE0 = mean((reg.pred-test.env$env1)^2)! W$ u. b3 v$ [/ G3 D& ?/ F
MSE0 $ Z, p+ W" O# J9 d, j( LNMSE0 = mean((test.env$env1-reg.pred)^2)/mean((test.env$env1-mean(test.env$env1))^2) M) N2 B r( {0 y& dNMSE0/ t9 f$ R$ t1 N5 v
% M1 F1 V9 ~' K) N4 D/ {
7 }9 t$ k+ H# s( T2 v1 J; G( P) y; {
图3|回归树构建结果,reg.tre。每个节点以整数标注,tree()默认树最大生长数值为31。因子变量的分类水平不能超过32。# g$ a' I6 k9 P
+ v, |5 K W1 B) T6 F 0 V5 W2 P v) H% @( ]* {! ^ % H( j6 V3 b& H9 f7 }图4|回归树输出结果,reg_tre_res.txt。var:用于拆分节点的变量及终端节点(<leaf>);n:每个节点的样本数量;dev:每个节点的偏差;yval:拟合结果,回归树为节点包含样本的因变量均值,分类树为该节点样本最多属于的分类水平;split: 节点拆分,2列分别是属于左侧或右侧的标签。, ~# r; y' n" e! ~7 \
: E6 U. S" P, e- L$ N6 | - w% a( b* M9 Y, A h# M& U1 f 7 v+ V0 G/ P2 B5 N+ I, x图5|回归树输出结果描述统计,reg.tre.res。包括终端节点数、拆分使用变量和残差平方和均值等信息。 , G! G8 u- o- E, V; X; p5 ~0 z* {2 d " a+ Q: v4 j6 h2 X' X" P$ \$ }4 n / n' q$ N+ v, ?) T+ p- p' n! L% E% v " O( s; {# ?* U: d& G) I! K, P$ `8 W图6|简单回归树绘图。回归决策树的每个节点上的数值是该节点处因变量的均值。 + [ @& X1 u8 {( S. k$ O/ |! R* J / L5 n8 x" ]3 f P/ y0 w' b! ]0 H
; F' i2 W2 ~/ u8 [: G
图7|测试数据因变量实际值与模型预测值散点图并添加趋势线。) u0 A1 G! n, z
9 D& F% F' A. [ P$ b/ S- o
W! D' p% I; v' A& T ]& F! s. f: X7 `3 B9 ~/ Q/ I2 {
图8|均方误差与标准化均方误差。评价模型预测好坏的一个准则为标准化均方误差(normalized mean squares error,NMSE)。+ t/ A% u/ f; o4 {, p8 e
& s. d+ Z5 m/ l' R
$ E% c; W- u; S4 t% X
: D4 k8 g. z# t0 [6 P3 e( n. k分母表示用最简单的算术平均来预测y的残差平方和。分子为该模型拟合后的残差平方和。此模型的NMSE不小于1,说明此回归模型没有任何意义(NMSE≥1)。此处是虚构数据,只讲使用方法,产生的模型没有任何意义,也没有影响。 ) l. S8 u `4 \9 e7 Y* g5 \: G8 B5 v5 _
2.2 优化模型-剪枝(Tree Pruning) . d7 P1 \5 R8 Q+ b1 T. F9 ~经过上述过程,构建的模型可能会过拟合,导致模型对训练集数据有很好的预测能力,但对测试集数据的预测能力较差。可能的原因是生成的决策树模型过于复杂。 % c; E" j; ?$ x8 V( i% ~5 q$ ^$ c: X0 q6 t' l8 G4 R. f5 E0 `
解决的方法之一是仅在拆分能使RSS降低值超过某个阈值的情况下才继续进行拆分。但是低于某个阈值的拆分点的之后的拆分点可能降低RSS的能力很强,所以不能随意剪枝。所以更好的优化模型的方式是先不设定RSS阈值,构建一个较大的决策树T0,然后根据某种方法对其进行剪枝获得子树。 5 V! ]3 K9 U, @. z3 |0 {' ~ . P* Q2 q' m j& K! b ?9 t剪枝的标准主要为获得的子树的错误率最低,常用交叉验证选择具有最低错误率的子树。但是子树集一般很大,所以一般限定在一个更小的子树集中进行交叉验证。这里引入一个新的概念复杂性代价剪枝(Cost complexity pruning,或最弱链接剪枝(weakest link pruning))。此时剪枝不考虑每棵子树,而只考虑由非负调整参数α索引的树序列。然后基于交叉验证选择α。α控制着树的复杂性及树与训练数据的适配性之间的权衡,当α=0时,子树T就是T0;当α的值逐渐增大时,表示拥有许多终端节点的树要付出的复杂性代价,因此终端节点越少,α值将会越小。 6 U2 [ T/ J0 |$ a, r0 A" C# |8 d. g% W% _/ B: ]
这里介绍两个定义方差(variance)与偏差(bias):1)方差是训练数据集的预测值或预测分类水平相对于其他数据集的预测值或预测分类水平的离散程度,代表了模型的泛化能力。2)偏差是模型的预测值或预测分类水平与训练数据中的实际值或实际分类水平之间的差别,代表了模型的预测准确性。模型构建要在方差与偏差之间权衡,使总体误差(偏差+方差)最小。. U8 R0 n e6 ^! k. N% U