查看原文
其他

用lasso回归构建生存模型+ROC曲线绘制

豆豆花花 生信星球 2022-06-06

 今天是生信星球陪你的第527天


   大神一句话,菜鸟跑半年。我不是大神,但我可以缩短你走弯路的半年~

   就像歌儿唱的那样,如果你不知道该往哪儿走,就留在这学点生信好不好~

   这里有豆豆和花花的学习历程,从新手到进阶,生信路上有你有我!

花花写于2020-02-02,看似雄赳赳气昂昂,其实不难。我们是R语言的应用者,不是开发者,所以诸如差异分析,富集分析,生存分析,绘图等等让初学者听起来遥不可及的操作,都只是一个R包,几个函数。打好基础,找到规律,任何函数都不难,不要被吓退

今天是千年一遇的对称日,去年各地民政局都表示宠网民,表示今天上半天班给登记结婚(今天周日本来不用上班的),不料赶上疫情,又纷纷取消了今天的登记活动,真的是造化弄人啊。湖北省更是全面暂停了婚姻登记业务,不知道该说什么,武汉加油,希望一切快点好起来,我还要再去讲课。

1.准备输入数据

输入数据是TCGA的表达矩阵expr、临床信息meta和group_list。保存为forest.Rdata了,在生信星球公众号后台聊天窗口回复“森林”即可获得。(为什么是森林呢,因为和随机森林图用的同一个数据,我又又又懒得改了。)

1load("forest.Rdata")
2exprSet = expr[,group_list=="tumor"]
3
4## 必须保证生存资料和表达矩阵,两者一致
5all(substring(colnames(exprSet),1,12)==meta$ID)
6#> [1] TRUE

2.构建lasso回归模型

lasso回归的输入数据是表达矩阵(仅含tumor样本)和对应的生死。

1x=t(log2(exprSet+1))
2y=meta$event
3library(glmnet)
4model_lasso <- glmnet(x, y,nlambda=10, alpha=1)
5print(model_lasso)
6#> 
7#> Call:  glmnet(x = x, y = y, alpha = 1, nlambda = 10) 
8#> 
9#>        Df    %Dev   Lambda
10#>  [1,]   0 0.00000 0.127800
11#>  [2,]   7 0.08689 0.076610
12#>  [3,]  22 0.19490 0.045930
13#>  [4,]  49 0.28660 0.027530
14#>  [5,]  97 0.42080 0.016510
15#>  [6,] 164 0.55460 0.009895
16#>  [7,] 217 0.66240 0.005932
17#>  [8,] 275 0.74850 0.003556
18#>  [9,] 345 0.81660 0.002132
19#> [10,] 403 0.86840 0.001278

这里是举例子,所以只计算了10个λ值,解释一下输出结果三列的意思。

  • Df 是自由度

  • 列%Dev代表了由模型解释的残差的比例,对于线性模型来说就是模型拟合的R^2(R-squred)。它在0和1之间,越接近1说明模型的表现越好,如果是0,说明模型的预测结果还不如直接把因变量的均值作为预测值来的有效。

  • Lambda 是构建模型的重要参数。

解释的残差百分比越高越好,但是构建模型使用的基因的数量也不能太多,需要取一个折中值。

2.1挑选合适的λ值

计算1000个,画图,筛选表现最好的λ值

1cv_fit <- cv.glmnet(x=x, y=y, nlambda = 1000,alpha = 1)
2plot.cv.glmnet(cv_fit)

两条虚线分别指示了两个特殊的λ值,一个是lambda.min,一个是lambda.1se,这两个值之间的lambda都认为是合适的。lambda.1se构建的模型最简单,即使用的基因数量少,而lambda.min则准确率更高一点,使用的基因数量更多一点。


2.2 用这两个λ值重新建模

1model_lasso_min <- glmnet(x=x, y=y, alpha = 1, lambda=cv_fit$lambda.min)
2model_lasso_1se <- glmnet(x=x, y=y, alpha = 1, lambda=cv_fit$lambda.1se)

这两个值体现在参数lambda上。有了模型,可以将筛选的基因挑出来了。所有基因存放于模型的子集beta中,用到的基因有一个s0值,没用的基因只记录了“.”,所以可以用下面代码挑出用到的基因。

1head(model_lasso_min$beta)
2#> 6 x 1 sparse Matrix of class "dgCMatrix"
3#>                         s0
4#> hsa-let-7a-1 -0.0003827849
5#> hsa-let-7a-2  .           
6#> hsa-let-7a-3  .           
7#> hsa-let-7b    .           
8#> hsa-let-7c    .           
9#> hsa-let-7d    .
10choose_gene_min=rownames(model_lasso_min$beta)[as.numeric(model_lasso_min$beta)!=0]
11choose_gene_1se=rownames(model_lasso_1se$beta)[as.numeric(model_lasso_1se$beta)!=0]
12length(choose_gene_min)
13#> [1] 33
14length(choose_gene_1se)
15#> [1] 12

3.模型预测和评估

3.1自己预测自己

newx参数是预测对象。输出结果lasso.prob是一个矩阵,第一列是min的预测结果,第二列是1se的预测结果,预测结果是概率,或者说百分比,不是绝对的0和1。

将每个样本的生死和预测结果放在一起,直接cbind即可。

1lasso.prob <- predict(cv_fit, newx=x , s=c(cv_fit$lambda.min,cv_fit$lambda.1se) )
2re=cbind(y ,lasso.prob)
3head(re)
4#>                              y         1         2
5#> TCGA-A3-3307-01A-01T-0860-13 0 0.1345889 0.2609906
6#> TCGA-A3-3308-01A-02R-1324-13 0 0.3984848 0.3547232
7#> TCGA-A3-3311-01A-02R-1324-13 1 0.3165679 0.3206726
8#> TCGA-A3-3313-01A-02R-1324-13 1 0.3996740 0.3487908
9#> TCGA-A3-3316-01A-01T-0860-13 0 0.3221988 0.2928797
10#> TCGA-A3-3317-01A-01T-0860-13 0 0.3740788 0.3733633

3.2 箱线图

对预测结果进行可视化。以实际的生死作为分组,画箱线图整体上查看预测结果。

1re=as.data.frame(re)
2colnames(re)=c('event','prob_min','prob_1se')
3re$event=as.factor(re$event)
4library(ggpubr) 
5p1 = ggboxplot(re, x = "event", y = "prob_min",
6               color = "event", palette = "jco",
7               add = "jitter")+ stat_compare_means()
8p2 = ggboxplot(re, x = "event", y = "prob_1se",
9          color = "event", palette = "jco",
10          add = "jitter")+ stat_compare_means()
11library(patchwork)
12p1+p2

可以看到,真实结果是死(0)的样本,预测的值就小一点(靠近0),真实结果是活着(1)的样本,预测的值就大一点(靠近1),整体上趋势是对的,但不是完全准确,模型是可用的。

对比两个λ值构建的模型,差别不大,1se的预测值不论生死都整体偏大一点点。

3.3 ROC曲线

计算AUC取值范围在0.5-1之间,越接近于1越好。可以根据预测结果绘制ROC曲线。横纵坐标分别是fpr和tpr。

1library(ROCR)
2library(caret)
3# 自己预测自己
4#min
5pred_min <- prediction(re[,2], re[,1])
6auc_min = performance(pred_min,"auc")@y.values[[1]]
7perf_min <- performance(pred_min,"tpr","fpr")
8plot(perf_min,colorize=FALSE, col="blue"
9lines(c(0,1),c(0,1),col = "gray", lty = 4 )
10text(0.8,0.2, labels = paste0("AUC = ",round(auc_min,3)))
1#1se
2pred_1se <- prediction(re[,3], re[,1])
3auc_1se = performance(pred_1se,"auc")@y.values[[1]]
4perf_1se <- performance(pred_1se,"tpr","fpr")
5plot(perf_1se,colorize=FALSE, col="red"
6lines(c(0,1),c(0,1),col = "gray", lty = 4 )
7text(0.8,0.2, labels = paste0("AUC = ",round(auc_min,3)))
  • 强迫症选项,想把两个模型画一起。

1plot(perf_min,colorize=FALSE, col="blue"
2plot(perf_1se,colorize=FALSE, col="red",add = T
3lines(c(0,1),c(0,1),col = "gray", lty = 4 )
4text(0.8,0.3, labels = paste0("AUC = ",round(auc_min,3)),col = "blue")
5text(0.8,0.2, labels = paste0("AUC = ",round(auc_1se,3)),col = "red")

-还想用ggplot2画的更好看一点

1tpr_min = performance(pred_min,"tpr")@y.values[[1]]
2tpr_1se = performance(pred_1se,"tpr")@y.values[[1]]
3dat = data.frame(tpr_min = perf_min@y.values[[1]],
4                 fpr_min = perf_min@x.values[[1]],
5                 tpr_1se = perf_1se@y.values[[1]],
6                 fpr_1se = perf_1se@x.values[[1]])
7library(ggplot2)
8ggplot() + 
9  geom_line(data = dat,aes(x = fpr_min, y = tpr_min),color = "blue") + 
10  geom_line(aes(x=c(0,1),y=c(0,1)),color = "grey")+
11  theme_bw()+
12  annotate("text",x = .75, y = .25,
13           label = paste("AUC of min = ",round(auc_min,2)),color = "blue")+
14  scale_x_continuous(name  = "fpr")+
15  scale_y_continuous(name = "tpr")
1ggplot() + 
2  geom_line(data = dat,aes(x = fpr_min, y = tpr_min),color = "blue") + 
3  geom_line(data = dat,aes(x = fpr_1se, y = tpr_1se),color = "red")+
4  geom_line(aes(x=c(0,1),y=c(0,1)),color = "grey")+
5  theme_bw()+
6  annotate("text",x = .75, y = .25,
7           label = paste("AUC of min = ",round(auc_min,2)),color = "blue")+
8  annotate("text",x = .75, y = .15,label = paste("AUC of 1se = ",round(auc_1se,2)),color = "red")+
9  scale_x_continuous(name  = "fpr")+
10  scale_y_continuous(name = "tpr")

5.切割数据构建模型并预测

5.1 切割数据

用R包caret切割数据,生成的结果是一组代表列数的数字,用这些数字来给表达矩阵和meta取子集即可。

1library(caret)
2set.seed(12345679)
3sam<- createDataPartition(meta$event, p = .5,list = FALSE)
4head(sam)
5#>      Resample1
6#> [1,]         5
7#> [2,]         9
8#> [3,]        13
9#> [4,]        17
10#> [5,]        19
11#> [6,]        22

可查看两组一些临床参数切割比例

1train <- exprSet[,sam]
2test <- exprSet[,-sam]
3train_meta <- meta[sam,]
4test_meta <- meta[-sam,]
5
6prop.table(table(train_meta$stage))
7#> 
8#>         i        ii       iii        iv 
9#> 0.4636015 0.1072797 0.2796935 0.1494253
10prop.table(table(test_meta$stage)) 
11#> 
12#>         i        ii       iii        iv 
13#> 0.5249042 0.1111111 0.1954023 0.1685824
14prop.table(table(test_meta$race)) 
15#> 
16#>                     asian black or african american 
17#>                0.01171875                0.08593750 
18#>                     white 
19#>                0.90234375
20prop.table(table(train_meta$race)) 
21#> 
22#>                     asian black or african american 
23#>                0.01937984                0.13953488 
24#>                     white 
25#>                0.84108527

5.2 切割后的train数据集建模

和上面的建模方法一样。

1#计算lambda
2x = t(log2(train+1))
3y = train_meta$event
4cv_fit <- cv.glmnet(x=x, y=y, nlambda = 1000,alpha = 1)
5plot.cv.glmnet(cv_fit)
1#构建模型
2model_lasso_min <- glmnet(x=x, y=y, alpha = 1, lambda=cv_fit$lambda.min)
3model_lasso_1se <- glmnet(x=x, y=y, alpha = 1, lambda=cv_fit$lambda.1se)
4#挑出基因
5head(model_lasso_min$beta)
6#> 6 x 1 sparse Matrix of class "dgCMatrix"
7#>              s0
8#> hsa-let-7a-1  .
9#> hsa-let-7a-2  .
10#> hsa-let-7a-3  .
11#> hsa-let-7b    .
12#> hsa-let-7c    .
13#> hsa-let-7d    .
14choose_gene_min=rownames(model_lasso_min$beta)[as.numeric(model_lasso_min$beta)!=0]
15choose_gene_1se=rownames(model_lasso_1se$beta)[as.numeric(model_lasso_1se$beta)!=0]
16length(choose_gene_min)
17#> [1] 48
18length(choose_gene_1se)
19#> [1] 3

6.测试集的模型预测

用训练集构建模型,预测测试集的生死,注意newx参数变了。

1lasso.prob <- predict(cv_fit, newx=t(log2(test+1)), s=c(cv_fit$lambda.min,cv_fit$lambda.1se) )
2re=cbind(test_meta$event ,lasso.prob)
3head(re)
4#>                                        1         2
5#> TCGA-A3-3307-01A-01T-0860-13 0 0.2760721 0.3084088
6#> TCGA-A3-3308-01A-02R-1324-13 0 0.3646723 0.3370767
7#> TCGA-A3-3311-01A-02R-1324-13 1 0.3190559 0.3292243
8#> TCGA-A3-3313-01A-02R-1324-13 1 0.3427763 0.3433195
9#> TCGA-A3-3317-01A-01T-0860-13 0 0.3489759 0.3589717
10#> TCGA-A3-3319-01A-02R-1324-13 0 0.7132020 0.3882291

再次进行可视化

1re=as.data.frame(re)
2colnames(re)=c('event','prob_min','prob_1se')
3re$event=as.factor(re$event)
4library(ggpubr) 
5p1 = ggboxplot(re, x = "event", y = "prob_min",
6               color = "event", palette = "jco",
7               add = "jitter")+ stat_compare_means()
8p2 = ggboxplot(re, x = "event", y = "prob_1se",
9          color = "event", palette = "jco",
10          add = "jitter")+ stat_compare_means()
11library(patchwork)
12p1+p2

目测还行哈?再看AUC值。

1library(ROCR)
2library(caret)
3# 训练集模型预测测试集
4#min
5pred_min <- prediction(re[,2], re[,1])
6auc_min = performance(pred_min,"auc")@y.values[[1]]
7perf_min <- performance(pred_min,"tpr","fpr")
8
9#1se
10pred_1se <- prediction(re[,3], re[,1])
11auc_1se = performance(pred_1se,"auc")@y.values[[1]]
12perf_1se <- performance(pred_1se,"tpr","fpr")
1tpr_min = performance(pred_min,"tpr")@y.values[[1]]
2tpr_1se = performance(pred_1se,"tpr")@y.values[[1]]
3dat = data.frame(tpr_min = perf_min@y.values[[1]],
4                 fpr_min = perf_min@x.values[[1]],
5                 tpr_1se = perf_1se@y.values[[1]],
6                 fpr_1se = perf_1se@x.values[[1]])
7
8ggplot() + 
9  geom_line(data = dat,aes(x = fpr_min, y = tpr_min),color = "blue") + 
10  geom_line(data = dat,aes(x = fpr_1se, y = tpr_1se),color = "red")+
11  geom_line(aes(x=c(0,1),y=c(0,1)),color = "grey")+
12  theme_bw()+
13  annotate("text",x = .75, y = .25,
14           label = paste("AUC of min = ",round(auc_min,2)),color = "blue")+
15  annotate("text",x = .75, y = .15,label = paste("AUC of 1se = ",round(auc_1se,2)),color = "red")+
16  scale_x_continuous(name  = "fpr")+
17  scale_y_continuous(name = "tpr")

主要看AUC值,可以说一般,也可以说还行。

插个小广告!

生信零基础入门学习小组长期报名中

GEO数据挖掘广州专场课程

再给生信技能树打个call!

一起来学单细胞

全球公益巡讲招学徒

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存