决策曲线分析DCA用于lasso回归/随机森林

本文首发于公众号:医学和生信笔记

医学和生信笔记,专注R语言在临床医学中的使用,R语言数据分析和可视化。主要分享R语言做医学统计学、meta分析、网络药理学、临床预测模型、机器学习、生物信息学等。

前面介绍了超多DCA的实现方法,基本上常见的方法都包括了,代码和数据获取方法也给了大家。

今天介绍的是如何实现其他模型的DCA,比如lasso回归、随机森林、决策树、SVM、xgboost等。

这是基于dca.r/stdca.r实现的一种通用方法,不过我在原本的代码上做了修改,原代码会在某些数据集报错。

  • 多个模型多个时间点DCA数据提取并用ggplot2画图
  • lasso回归的DCA
  • 随机森林的DCA

多个时间点多个cox模型的数据提取

其实ggDCA包完全可以做到,只要1行代码就搞定了,而且功能还很丰富。

我给大家演示一遍基于stdca.r的方法,给大家开阔思路,代码可能不够简洁,但是思路没问题,无非就是各种数据整理与转换。

而且很定会有人对默认结果不满意,想要各种修改,下面介绍的这个方法非常适合自己进行各种自定义!

rm(list = ls())
library(survival)
library(dcurves)
data("df_surv")

# 加载函数
source("../000files/stdca.R") # 原函数有问题

# 构建一个多元cox回归
df_surv$cancer <- as.numeric(df_surv$cancer) # stdca函数需要结果变量是0,1
df_surv <- as.data.frame(df_surv) # stdca函数只接受data.frame

# 建立多个模型
cox_fit1 <- coxph(Surv(ttcancer, cancer) ~ famhistory+marker, data = df_surv)
cox_fit2 <- coxph(Surv(ttcancer, cancer) ~ age + famhistory + marker, data = df_surv)
cox_fit3 <- coxph(Surv(ttcancer, cancer) ~ age + famhistory, data = df_surv)

# 计算每个模型在不同时间点的概率
df_surv$prob11 <- c(1-(summary(survfit(cox_fit1, newdata=df_surv), times=1)$surv))
df_surv$prob21 <- c(1-(summary(survfit(cox_fit2, newdata=df_surv), times=1)$surv))
df_surv$prob31 <- c(1-(summary(survfit(cox_fit3, newdata=df_surv), times=1)$surv))

df_surv$prob12 <- c(1-(summary(survfit(cox_fit1, newdata=df_surv), times=2)$surv))
df_surv$prob22 <- c(1-(summary(survfit(cox_fit2, newdata=df_surv), times=2)$surv))
df_surv$prob32 <- c(1-(summary(survfit(cox_fit3, newdata=df_surv), times=2)$surv))

df_surv$prob13 <- c(1-(summary(survfit(cox_fit1, newdata=df_surv), times=3)$surv))
df_surv$prob23 <- c(1-(summary(survfit(cox_fit2, newdata=df_surv), times=3)$surv))
df_surv$prob33 <- c(1-(summary(survfit(cox_fit3, newdata=df_surv), times=3)$surv))

计算threshold和net benefit:

cox_dca1 <- stdca(data = df_surv, 
      outcome = "cancer", 
      ttoutcome = "ttcancer", 
      timepoint = 1, 
      predictors = c("prob11","prob21","prob31"),
      smooth=TRUE,
      graph = FALSE
    )

cox_dca2 <- stdca(data = df_surv, 
      outcome = "cancer", 
      ttoutcome = "ttcancer", 
      timepoint = 2, 
      predictors = c("prob12","prob22","prob32"),
      smooth=TRUE,
      graph = FALSE
    )

cox_dca3 <- stdca(data = df_surv, 
      outcome = "cancer", 
      ttoutcome = "ttcancer", 
      timepoint = 3, 
      predictors = c("prob13","prob23","prob33"),
      smooth=TRUE,
      graph = FALSE
    )


library(tidyr)
library(dplyr)

第一种数据整理方法

cox_dca_df1 <- cox_dca1$net.benefit
cox_dca_df2 <- cox_dca2$net.benefit
cox_dca_df3 <- cox_dca3$net.benefit

names(cox_dca_df1)[2] <- "all1"
names(cox_dca_df2)[2] <- "all2"
names(cox_dca_df3)[2] <- "all3"

tmp <- cox_dca_df1 %>% 
  left_join(cox_dca_df2) %>% 
  left_join(cox_dca_df3) %>% 
  pivot_longer(cols = contains(c("all","sm","none")),
               names_to = "models",
               values_to = "net_benefit"
               )

画图:

library(ggplot2)
library(ggsci)

ggplot(tmp, aes(x=threshold,y=net_benefit))+
  geom_line(aes(color=models),size=1.2)+
  scale_x_continuous(labels = scales::label_percent(accuracy = 1),
                     name="Threshold Probility")+
  scale_y_continuous(limits = c(-0.05,0.3),name="Net Benefit")+
  theme_bw(base_size = 14)
image.png

第二种数据整理方法

cox_dca_df1 <- cox_dca1$net.benefit
cox_dca_df2 <- cox_dca2$net.benefit
cox_dca_df3 <- cox_dca3$net.benefit

cox_dca_long_df1 <- cox_dca_df1 %>% 
  rename(mod1 = prob11_sm,
         mod2 = prob21_sm,
         mod3 = prob31_sm
         ) %>% 
  select(-4:-6) %>% 
  mutate(time = "1") %>% 
  pivot_longer(cols = c(all,none,contains("mod")),names_to = "models",
               values_to = "net_benefit"
               )

cox_dca_long_df2 <- cox_dca_df2 %>% 
  rename(mod1 = prob12_sm,
         mod2 = prob22_sm,
         mod3 = prob32_sm
         ) %>% 
  select(-4:-6) %>% 
  mutate(time = "2") %>% 
  pivot_longer(cols = c(all,none,contains("mod")),names_to = "models",
               values_to = "net_benefit"
               )


cox_dca_long_df3 <- cox_dca_df3 %>% 
  rename(mod1 = prob13_sm,
         mod2 = prob23_sm,
         mod3 = prob33_sm
         ) %>% 
  select(-4:-6) %>% 
  mutate(time = "3") %>% 
  pivot_longer(cols = c(all,none,contains("mod")),names_to = "models",
               values_to = "net_benefit"
               )

tes <- bind_rows(cox_dca_long_df1,cox_dca_long_df2,cox_dca_long_df3)

画图:

ggplot(tes,aes(x=threshold,y=net_benefit))+
  geom_line(aes(color=models,linetype=time),size=1.2)+
  scale_x_continuous(labels = scales::label_percent(accuracy = 1),
                     name="Threshold Probility")+
  scale_y_continuous(limits = c(-0.05,0.3),name="Net Benefit")+
  theme_bw(base_size = 14)
image.png

这种方法可以分面。

ggplot(tes,aes(x=threshold,y=net_benefit))+
  geom_line(aes(color=models),size=1.2)+
  scale_y_continuous(limits = c(-0.05,0.3),name="Net Benefit")+
  scale_x_continuous(labels = scales::label_percent(accuracy = 1),
                     name="Threshold Probility")+
  scale_y_continuous(limits = c(-0.05,0.3),name="Net Benefit")+
  theme_bw(base_size = 14)+
  facet_wrap(~time)

image.png

接下来演示其他模型的DCA实现方法,这里就以二分类变量为例,生存资料的DCA也是一样的,就是需要一个概率而已!

lasso回归

rm(list = ls())
suppressMessages(library(glmnet))
suppressPackageStartupMessages(library(tidyverse))

准备数据,这是从TCGA下载的一部分数据,其中sample_type是样本类型,1代表tumor,0代表normal,我们首先把因变量变为0,1。然后划分训练集和测试集。

df <- readRDS(file = "df_example.rds")

df <- df %>% 
  select(-c(2:3)) %>% 
  mutate(sample_type = ifelse(sample_type=="Tumor",1,0))

ind <- sample(1:nrow(df),nrow(df)*0.6)

train_df <- df[ind,]
test_df <- df[-ind,]

构建lasso回归需要的参数值。

x <- as.matrix(train_df[,-1])
y <- train_df$sample_type

建立lasso回归模型:

cvfit = cv.glmnet(x, y, family = "binomial")
plot(cvfit)
image.png

在测试集上查看模型表现:

prob_lasso <- predict(cvfit,
                      newx = as.matrix(test_df[,-1]),
                      s="lambda.1se",
                      type="response") #返回概率

然后进行DCA,也是基于训练集的:

source("../000files/dca.r")

test_df$lasso <- prob_lasso

df_lasso <- dca(data = test_df, # 指定数据集,必须是data.frame类型
    outcome="sample_type", # 指定结果变量
    predictors="lasso", # 指定预测变量
    probability = T
    )
image.png

这就是lasso的DCA,由于数据和模型原因,这个DCA看起来很诡异,大家千万要理解实现方法!

library(ggplot2)
library(ggsci)
library(tidyr)

df_lasso$net.benefit %>% 
  pivot_longer(cols = -threshold, 
               names_to = "type", 
               values_to = "net_benefit") %>% 
  ggplot(aes(threshold, net_benefit, color = type))+
  geom_line(size = 1.2)+
  scale_color_jama(name = "Model Type")+ 
  scale_y_continuous(limits = c(-0.02,1),name = "Net Benefit")+ 
  scale_x_continuous(limits = c(0,1),name = "Threshold Probility")+
  theme_bw(base_size = 16)+
  theme(legend.position = c(0.2,0.3),
        legend.background = element_blank()
        )
image.png

随机森林

library(ranger)
rf <- ranger(sample_type ~ ., data = train_df)
prob_rf <- predict(rf,test_df[,-1],type = "response")$predictions

test_df$rf <- prob_rf

df_rf <- dca(data = test_df, # 指定数据集,必须是data.frame 
outcome="sample_type", # 指定结果变量    
predictors="rf", # 指定预测变量   
 probability = T,    
graph = F    )

画图:

df_rf$net.benefit %>% 
  pivot_longer(cols = -threshold, 
               names_to = "type", 
               values_to = "net_benefit") %>% 
  ggplot(aes(threshold, net_benefit, color = type))+
  geom_line(size = 1.2)+
  scale_color_jama(name = "Model Type")+ 
  scale_y_continuous(limits = c(-0.02,1),name = "Net Benefit")+ 
  scale_x_continuous(limits = c(0,1),name = "Threshold Probility")+
  theme_bw(base_size = 16)+
  theme(legend.position = c(0.2,0.3),
        legend.background = element_blank()
        )
image.png

还有其他比如k最近邻、支持向量机等等等等,就不一一介绍了,实现原理都是一样的,就是需要一个概率而已。

需要修改后的 stdca.r 脚本的,赞赏5元,截图发我即可~

理论上只要能算出概率,都是能画决策曲线的,但是如何解释是很大的问题!

本文首发于公众号:医学和生信笔记

医学和生信笔记,专注R语言在临床医学中的使用,R语言数据分析和可视化。主要分享R语言做医学统计学、meta分析、网络药理学、临床预测模型、机器学习、生物信息学等。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容