应用生存模型

杰奎琳·布罗Novik

2016-06-22

生存建模是任何临床数据分析工具集的核心组件。

在这里,我们将通过施用STAN的生存模型的示例,作为来自膀胱尿路上皮癌患者的TCGA的示例数据。

《Stan》中的生存模式相当简单。然而,在Stan中指定模型需要比使用标准MLE工具(如苏克福特

这个小插图是一个多部分系列的第1部分。

大纲

在几个小插图的过程中,我们将回顾各种模型和分析方法。

在这篇小短文中,我们介绍了以下内容:

  1. 数据审核及检查
  2. 回顾参数Weibull生存模型
  3. 测试参数生存模型对模拟数据
  4. 用TCGA数据拟合NULL参数生存模型
  5. 对模型进行收敛性检验和后验预测检验

本系列的第二部分将考虑一个非参数分段指数模型。

数据

我们将使用来自癌症基因组图集(TCGA)用于诊断为膀胱尿路上皮癌的患者这个群体有个首字母缩写BLCA

TCGA是一个数据库,提供各种癌症类型的临床和分子数据,主要关注基因组数据集和标准治疗干预后的结果。这里描述了BLCA队列这篇论文

完整的临床和分子数据可从TCGA数据门户网获得,但在这种情况下,我们将使用可从此数据的策划版本http://www.cbioportal.org/

要将数据加载到R中,我们可以通过web-url服务查询这些数据:

url < -'http://www.cbioportal.org/webservice.do?cmd= getclinicaldata&case_set_id=blca_tcga_all'要求< -httr::得到(url) clinical_data < - - - - - -httr::内容(要求,类型=“文本/标签分隔值”,col_names =T,col_types =)str(clinical_data)

然而,MSKCC提供了CGDS-R包,它为相同的数据提供了一个更简单的接口。

运行以下代码行以加载此队列的临床数据:

mycgds =cgdsr::CGDS.(“http://www.cbioportal.org/public-portal/”) selected_case_list =“blca_tcga_all”clinical_data =cgdsr::getClinicalData(mycgds selected_case_list)

描述可用数据的结构:

str(clinical_data不。列表=T,vec.len =.2)
##'data.frame':413 OBS。75个变量:## $ Age:int 77 73 67 84 78 ... ## $ ajcc_metastasis_pathologic_pm:chr“mx”m0“... ## $ ajcc_nodes_pathologic_pn:chr”n0“”n0“... ##$ ajcc_pathologic_tumor_stage:chr“阶段二”“阶段III”... ## $ AJCC_STAGED_EDITION:CHR“7th”“7th”... ## $ AJCC_TUMOR_PATHOLOGIC_PT:CHR“T2A”“T3”... ## $ Angiolymphic_invasion:Chr“no”“no”... ## $ clin_t_stage:chr“”“......'... ## $ days_to_birth:int -28204 -27000 -24625 -30686 -28757 ... ## $ days_to_collection:int 99 14984 447 1157 ... ## $ days_to_death:int na na na 328 na ... ## $ days_to_initial_pathologic_diagnosis:int 0 0 0 0 0 ... ## $ days_to_last_followup:int 162 28 0 na 1248 ... ##$ dfs_months:num 19.05 0.92 ... ## $ dfs_status:chr“fourtalfree”“suffactfree”... ## $ ECOG_SCORE:CHR“”“1”... ## $种族:CHR“不是西班牙裔或拉丁裔”“不是hispanic或latino”... ## $ family_history_cancer_relationship:chr“”“... ## $ family_history_cancer_type:chr”“”“... ## $ form_completion_date:chr“2012-8-27”“2013-12-16”... ## $性别:Chr“男性”“男性”... ## $ Height:Num 180 173 170 188 Na ...##$ sityology_subtype:chr“non-papillary”“papillary”... ## $ Havily_Neoadjuvant_trtyn:Chr“否”“否”... ## $ Haver_other_Malignancy:Chr“是”“是”... ## $ ICD_10:CHR“C67.9”“C67.0”... ## $ ICD_O_3_HISTOLOGY:CHR“8120/3”“8120/3”... ## $ ICD_O_3_SITE:CHR“C67.9”“C67.0”... ## $ Informed_consent_verified:chr“是”“是”... ## $ initial_pathologic_dx_year:int 2012 2013 2013 2011 2011 2011 2009 ... ## $ initial_weight:int 500 1470 320 910 480 ... ## $ IS_FFPE : chr "NO" "NO" ... ## $ KARNOFSKY_PERFORMANCE_SCORE : chr "90" "80" ... ## $ LYMPH_NODES_EXAMINED : chr "YES" "YES" ... ## $ LYMPH_NODES_EXAMINED_HE_COUNT : int 0 0 NA 0 0 ... ## $ LYMPH_NODE_EXAMINED_COUNT : int 19 14 NA 40 19 ... ## $ METHOD_OF_INITIAL_SAMPLE_PROCUREMENT : chr "Transurethral resection (TURBT)" "Transurethral resection (TURBT)" ... ## $ METHOD_OF_INITIAL_SAMPLE_PROCUREMENT_OTHER: chr "" "" ... ## $ NEW_TUMOR_EVENT_AFTER_INITIAL_TREATMENT : chr "NO" "NO" ... ## $ NONINVASIVE_BLADDER_CA_TX_TYPE : chr "" "Transurethral resection alone" ... ## $ NONINVASIVE_BLADDER_HISTORY : chr "YES" "YES" ... ## $ OCCUPATION_CURRENT : chr "" "Retired" ... ## $ OCCUPATION_PRIMARY : chr "" "" ... ## $ OCCUPATION_PRIMARY_CHEMICAL_EXPOSURE : chr "Secondhand" "" ... ## $ OCCUPATION_PRIMARY_INDUSTRY : chr "" "" ... ## $ OCCUPATION_PRIMARY_YEARS_WORKED : int NA NA NA NA NA ... ## $ OCT_EMBEDDED : chr "true" "true" ... ## $ OS_MONTHS : num 19.05 0.92 ... ## $ OS_STATUS : chr "LIVING" "LIVING" ... ## $ OTHER_PATIENT_ID : chr "10422FE9-8D70-40A9-9875-3FE9FAB68823" "6F30B069-C8F5-424C-9FB5-F7DBA508CB8B" ... ## $ OTHER_SAMPLE_ID : chr "FE3F7C73-8E04-48BA-AF35-E369F5C62E98" "784E85BB-9C4C-44B4-8BFC-3B2DBC1406C8" ... ## $ PATHOLOGY_REPORT_FILE_NAME : chr "TCGA-GV-A40G.28754343-DA2B-4608-A66C-B4E41B52F6FF.pdf" "TCGA-E7-A7DU.BEB0E95C-EDF2-40A5-BB95-588D07369CEE.pdf" ... ## $ PATHOLOGY_REPORT_UUID : chr "28754343-DA2B-4608-A66C-B4E41B52F6FF" "BEB0E95C-EDF2-40A5-BB95-588D07369CEE" ... ## $ PHARMACEUTICAL_TX_ADJUVANT : chr "NO" "[Unknown]" ... ## $ PROSPECTIVE_COLLECTION : chr "YES" "YES" ... ## $ RACE : chr "WHITE" "WHITE" ... ## $ RADIATION_TREATMENT_ADJUVANT : chr "NO" "NO" ... ## $ RETROSPECTIVE_COLLECTION : chr "NO" "NO" ... ## $ SAMPLE_TYPE : chr "Primary Tumor" "Primary Tumor" ... ## $ SAMPLE_TYPE_ID : int 1 1 1 1 1 ... ## $ SMOKING_PACK_YEARS : num 30 NA 30 40 20 ... ## $ SMOKING_YEAR_STARTED : int 18 NA 37 NA NA ... ## $ SMOKING_YEAR_STOPPED : int 1997 1980 2012 1992 1950 ... ## $ TIME_TO_COMPLETE_RESPONSE : int NA NA NA NA NA ... ## $ TISSUE_SOURCE_SITE : chr "GV" "E7" ... ## $ TOBACCO_SMOKING_HISTORY_INDICATOR : chr "4" "3" ... ## $ TREATMENT_OUTCOME_FIRST_COURSE : chr "Complete Remission/Response" "Complete Remission/Response" ... ## $ TUMOR_STATUS : chr "TUMOR FREE" "TUMOR FREE" ... ## $ TX_90DAYS_PRIOR_TO_RESECTION : chr "" "" ... ## $ TX_COMPLETE_RESPONSE : chr "" "" ... ## $ TX_INDUCTION_COURSES_INDICATOR : chr "" "" ... ## $ TX_MAINTENANCE_COURSES_INDICATOR : chr "" "" ... ## $ VIAL_NUMBER : chr "A" "A" ... ## $ VITAL_STATUS : chr "Alive" "Alive" ... ## $ WEIGHT : num 82 110 52 80 NA ...

数据清理

让我们对这个DataFrame做一些最小的数据操作,以使它更容易使用。我个人喜欢用小写的变量名,用NA值代替空字符串。

我们将把转换后的数据保存在一个名为data.frame的文件中clin_data

##名称小写的名字(clinical_data) < -放低(的名字(临床_data))##转换空字符串 - > na值convert_blank_to_na < -函数(x) {if (!purrr::is_character(x)) {警告('输入向量不是字符-返回原始输入')返回(x)} else {ifelse(x = =,NA, x)}} clin_data <-clinical_data % > %dplyr::变异_each.(有趣的人=有趣的人(convert_blank_to_na),一切##检查结果数据帧str(clin_datavec.len =.2,列表。len =10)
##'data.frame':413 OBS。75个变量:## $ Age:int 77 73 67 84 78 ... ## $ ajcc_metastasis_pathologic_pm:chr“mx”m0“... ## $ ajcc_nodes_pathologic_pn:chr”n0“”n0“... ##$ ajcc_pathologic_tumor_stage:chr“阶段二”“阶段III”... ## $ AJCC_STAGED_EDITION:CHR“7th”“7th”... ## $ AJCC_TUMOR_PATHOLOGIC_PT:CHR“T2A”“T3”... ## $ Angiolymphic_invasion:Chr“否”“否”... ## $ Clin_t_stage:Chr Na ... ## $ Days_to_Birth:int -28204 -27000 -24625 -30686 -28757 ... ## $ Days_to_collection:int 99 149 84 4471157 ... ## [列表输出截断]

数据探索

在生存分析中,结果或因变量是时间事件有些事件时间不被观察(IE他们被审查)。

在这里,我们考虑更常见的情景right-censoring。这是没有观察到终止事件的情况。相反,观察结果不时受到审查t

我们的第一个分析将治疗总生存期作为感兴趣的事件,而不是无进展生存。在这个队列中,总生存率用两个变量来描述:os_status.&os_months

我们将首先检查这些数据。

负面/失踪事件

我们有一个缺失数据的观测os_status.:

clinical_data % > %dplyr::筛选(is.na(os_status) |os_status = =) % > %dplyr::选择(os_status os_months) % > %str()
## data.frame: 1 obs## $ os_status: chr "" ## $ os_months: num NA

我们有3个未知和/或负生存时间的观察结果(os_months <0.):

clinical_data % > %dplyr::筛选(!is.na(os_status) &os_status ! =) % > %dplyr::筛选(os_months <.0|is.na(os_months)) % > %dplyr::选择(os_status os_months) % > %()
## os_status os_months ## 1 Decease Na ## 2 Deced Na ## Living -2.1

现在,我们从分析数据集(clin_data)。

clin_data < -clin_data % > %dplyr::筛选(!is.na(os_status) &os_status ! =) % > %dplyr::筛选(os_months> =0&!is.na(os_months)证实了比原来少了4个观察结果assert_that.(nrow(clin_data) = =nrow(clinical_data) -4)
## [1]真实

传播事件时间

在其余的观察中,发生事件的中位时间为17.61个月。

事件时间分布如下观察(已故)和审查(生活)观察:

ggplot(clin_dataaes(X =os_months,组=os_status,颜色=os_status,填补=os_status)) +geom_density(α=0.5)

这些观测值的KM曲线看起来是这样的:

(使用苏克福特)

大中型企业。surv < -苏克福特(Surv(os_months os_deceased) ~1,data =clin_data % > %dplyr::变异(os_deceased =os_status = =“死者”))autoplot.(Mle.surv,conf.int =f)+ggtitle.(“BLCA队列KM生存曲线”)

第一次分析:参数生存模型

对于我们的第一次分析,我们将使用参数威布尔生存模型。

我们将从模型代码开始wei_bg.stangithub repo.陪同Peltola等人,2014这篇文章描述了生物标志物评价的贝叶斯方法。

该模型假设事件发生的时间x服从威布尔分布。

Stan将这个概率密度函数参数化为:

\ [f (x | \α\σ)= \压裂{\α}{\σ}\离开(\压裂{x}{\σ}\右)^ ^ {\ alpha -} e {- (x / \σ)^{\α}}\]

在此分析中,我们将定义两个参数:

如果我们有协变量并且想要估计一个比例风险模型,我们将替换μ用协变量的线性组合。然而,在这种情况下,我们感兴趣的是恢复我们的NULL模型的特性,所以我们处理μ作为常数截距。

模型的Stan代码

此模型的stan代码包含在其中biostan包作为weibull_survival_null_model.stan

它可以通过调用来访问system.file(),如:

如果(!需要(biostan)) devtools::install_github(“jburos / biostan”)图书馆(Biostan)stan_file < -执行(“斯坦”,“weibull_survival_null_model.stan”,包=“biostan”)

以下是该文件的内容:

biostan::print_stan_file.(stan_file)
## /*变量命名:## obs = observed ## cen = (right)审查## N =样本数量## tau = scale parameter ## */ ## data {## int Nobs;# # int低< = 0 >商品;# #向量(脑袋)社会毒瘤;# #向量(商品)ycen;##} ## ## transform data {## real tau_mu;# #真正的低< = 0 > tau_al;## ## tau_mu <- 10.0;## tau_al <- 10.0;##} ## ##参数{## real alpha_raw;# #真正的μ; ## } ## ## transformed parameters { ## real alpha; ## alpha <- exp(tau_al * alpha_raw); ## } ## ## model { ## yobs ~ weibull(alpha, exp(-(mu)/alpha)); ## increment_log_prob(weibull_ccdf_log(ycen, alpha, exp(-(mu)/alpha))); ## ## alpha_raw ~ normal(0.0, 1.0); ## mu ~ normal(0.0, tau_mu); ## } ##

详细的模型

在使用这个模型进行分析之前,我们想首先详细地审查模型代码并针对一些模拟数据测试它。

这将确保(a)我们理解模型良好,(b)模型可以从模拟数据中恢复估计。

(正如您将看到的,模拟数据过程的几个部分也可以重新用于后验预测检查。因此,我们将保存该流程的组件,以便在以后的步骤中重用。)

如果你在R控制台,你可以在编辑器中打开Stan文件,如下所示:

如果(互动())file.edit(stan_file)

审查数据块

首先让我们回顾一下数据块。

这将告诉我们模型输入数据的结构和格式。

print_stan_file.(stan_file节=“数据”)
##数据{## int  nobs;# # int低< = 0 >商品;# #向量(脑袋)社会毒瘤;# #向量(商品)ycen;# #}

被审查和观察的数据点作为单独的输入向量提供。

观察到的数据点

  • 脑袋:观测数据点数目
  • 社会毒瘤:观察到事件的次数

审查数据点

  • 商品:删减数据点的数目
  • ycen:被审查的事件

回想一下,这是一个NULL模型(没有协变量值),所以不需要观察到的协变量的数量和值。

评估模型块

在线性预测器中,stan代码包含一个隐式常数项μ

print_stan_file.(stan_file节='模型')
##模型{## yobs ~ weibull(alpha, exp(-(mu)/alpha)));## increment_log_prob(weibull_ccdf_log(ycen, alpha, exp(-(mu)/alpha)));## ## alpha_raw ~ normal(0.0, 1.0);## mu ~ normal(0.0, tau_mu);# #}

观察CCDF(互补累​​积分布函数)如何使用审查观测的日志概率来计算。

在这种情况下,ccdf代表什么?

模型如何处理截尾过程?

检查参数块

我们的斯坦代码还包含了一个Reparameterizationα项,在转换参数块。

观察:

print_stan_file.(stan_file节=的转换参数)
##转换参数{##真实alpha;## alpha <- exp(tau_al * alpha_raw);# #}

(回想一下,tau_al常数缩放项是否设置为10alpha_raw是具有正常(0,1)的先前分配的参数)。

这种重新参数化实现了两个目的:

  1. 的使用tau_al * alpha_raw是a的例子吗明确参数化
    • 它应该是在数学上相当于定义一个(未转换的)参数α与之前的正常(0,10)
    • 然而,此参数化将产生一个参数(alpha_raw),其规模与我们模型中的其他参数相似。的exp ()转变使这两种尺度之间的差异更加显著。
    • 一般来说,所有参数在一个相似的尺度上使抽样更有效。
  2. exp ()这个参数的转换另外允许我们放一个先验日志α
    • 我们要加一个优先权日志α因为进入指数的可能性。

看来要做的事太多了。

然而,它对我们的建模效率有实际意义。

观察到一个值alpha_raw(例如0.2),转换产生:

alpha_raw < -0.2tau_al < -10log_alpha < -alpha_raw *tau_alα< -经验值(log_alpha)打印(α)
# # 7.389056 [1]

这可能看起来很傻。

然而

考虑通过一系列值的Alpha分布alpha_raw从我们的采样正常(0,1)之前:

alpha_raw < -rnorm(1000,0,1) tau_al < -10log_alpha < -alpha_raw *tau_alα< -经验值(log_alpha)ggplot(data.frame(α=α,alpha_raw =alpha_raw),aes(X =alpha))+geom_density() +scale_x_log10(标签=科学)

注意α取值范围为1e-10到1e+10。我们必须大幅度地截断它来考虑在原来的尺度上画它。

采样这个参数空间可能需要不同的步长和在整个分布中不同的调优参数值。

alpha_raw相比之下,缩放是一个很多比较友好。

ggplot(data.frame(α=α,alpha_raw =alpha_raw),aes(X =α,y =alpha_raw)) +geom_density2d() +scale_x_log10(标签=科学)

该分布以0为中心,在其取值范围内具有更一致的行为。

这里需要注意的是,虽然非中心参数化在数学上等同于标准参数化,但它(在某些方面)是一个不同的模型。考虑重新arameterization将影响参数值的后验估计值。

包就像rstanarm它为在Stan中实现的各种标准模型提供了简单的包装,默认情况下使用非中心参数化。

更多关于非中心参数化的信息:

  1. 讨论STAN-DEV列表
  2. Gelman,2004.参数化和贝叶斯建模

用模拟数据对模型进行测试

现在我们已经审查了模型代码,我们准备根据这个模型模拟数据。

我们可以用R或Stan来模拟数据。我们将从模拟R中的数据开始。

模拟R中的数据

与我们的STAN模型代码一样,我们最初基于这种功能的例子。R文件从stan-survival-shrinkage github回购

不过,经过进一步的检查(见相关资料weibull-survival-model我们稍微修改了模拟数据函数。这里我们将使用修改后的函数。

我们的weibull_sim_data函数接受两个参数(αμ)作为输入和所需的样本大小(n)。它返回模拟事件时间的数据帧。

weibull_sim_data < -Function (alpha, mu, n) {data <-data.frame(surv_months =rweibull(n =n,α,经验值(-(μ)/α)),censor_months =rexp(n =n,率=1/One hundred.),stringsAsFactors =f)%>%dplyr::变异(os_status =ifelse(surv_months <.censor_months,“死者”,“生活”),os_months =ifelse(surv_months <.surv_months, Censor_months返回(数据)}

关于这个函数的一些注释:

  1. 注意一下审查过程rexp ()。我们的选择有点武断。
    • 通常,我们的Stan模型不知道截尾过程,除了假设截尾是无信息的。
  2. 我们还故意写入此功能以模仿我们的临床数据的结构。

这将使以后重用This &其他函数变得更容易。

模拟任意输入值的数据

我们可以使用这个函数来模拟假设的参数值的数据集α&μ

test_alpha < -0.8test_mu < --3.##从TCGA的blca数据test_n <-的样本大小nrow(clin_data) ##测试这些输入的alpha和mu模拟数据<-的任意值weibull_sim_data(α=test_alpha,穆=test_mu,n =test_n)(simulated_data)
## surv_months censor_months os_status os_months ## 1 0.5995253 15.65978 DECEASED 0.995253 ## 2 24.4544603 62.24349 DECEASED 24.4544603 ## 3 5.9572821 87.10862 DECEASED 5.9572821 87.10862 DECEASED 5.9572821 ## 4 29.3960125 32.69540 DECEASED 29.3960125 # 5 19.1953454 94.13512 DECEASED 19.1953454 ## 6 207.2250962 343.31931 DECEASED 207.2250962

模拟生存曲线如下:

##从模拟数据中绘制KM曲线simulated_data%>%dplyr::变异(os_deceased =os_status = =“死者”)autoplot.(生存::苏克福特(Surv(os_months,os_deced)〜1,data =simulated_data),conf.int =f)+ggtitle.('模拟km curve')

拟合Stan模拟数据

现在我们已经模拟了数据,我们已经准备好在Stan中拟合模型了。

如果我们正确地编写了stan代码和模拟数据处理,我们的后验间隔为αμ应该包含用于模拟数据集(0.8和-3)的值。

为Stan准备数据

Stan将数据输入作为列表。列表的内容应与那些相匹配数据在Stan代码中阻塞。

例如,查看数据块-

print_stan_file.(stan_file节=“数据”)
##数据{## int  nobs;# # int低< = 0 >商品;# #向量(脑袋)社会毒瘤;# #向量(商品)ycen;# #}

我们给Stan的输入列表应该分别包含观察和审查数据的尺寸和值。

observed_data < -simulated_data%>%dplyr::筛选(os_status = =“死者”) censored_data < -simulated_data%>%dplyr::筛选(os_status!=“死者”) stan_data < -列表(脑袋=nrow(observed_data),商品=nrow(被审查的_data),社会毒瘤=observed_data os_months美元,ycen =censored_data os_months美元)rm(censored_data)rm(观察到_data)str(stan_data)
## List of 4 ## $ Nobs: int 292 ## $ nen: int 117 ## $ yobs: num[1:292] 0.6 24.45 5.96 29.4 19.2…## $ ycen: num[1:17] 33.17 9.95 17.57 6.31 150.28…

(在函数中包装此预备数据流程gen_stan_data为以后)

gen_stan_data < -函数(data) {observed_data <-数据% > %dplyr::筛选(os_status = =“死者”) censored_data < -数据% > %dplyr::筛选(os_status!=“死者”) stan_data < -列表(脑袋=nrow(observed_data),商品=nrow(被审查的_data),社会毒瘤=observed_data os_months美元,ycen =CISCORED_DATA $ OS_MONTHS)}

用Stan测试模拟值

我们叫斯坦:

recover_simulated < -rstan::斯坦(stan_filedata =gen_stan_data(simulated_data),链=4,iter =1000,种子=1328025050)打印(recover_simulated)
## Stan模型的推理:weibull_surviv_null_model。## 4链,每个iter=1000;热身= 500;瘦= 1;每个链的热身后抽牌量=500,总抽牌量=2000## ## mean se_mean sd 2.5% ## alpha_raw 6.000000e-02 9.000000e-02 1.300000e-01 -3.000000e-02 ## mu -2.630000e+00 7.100000e-01 1.010000e+00 -3.500000e+00 ## alpha 5.210000e+00 5.350000e+00 7.57000e +00 7.600000e-01 ## lp__ -1.220654e+45 1.494616e+45 2.114767e+45 -4.892679e+45 ## 25% 50% 75% 97.5% n_eff r# # alpha_raw -2.000000e-02 -0.02 0.07 0.2923.3.。82 ## mu -3.270000e+00 -3.14 -2.17 -0.89 2 7.38 ## alpha 8.200000e-01 0.86 5.31 18.32 2 228.85 ## lp__ -1.217518e+45 -1400.52 -1399.71 -1399.34 2 1240.90 ## ## Samples were drawn using NUTS(diag_e) at Wed Jun 22 22:20:40 2016. ## For each parameter, n_eff is a crude measure of effective sample size, ## and Rhat is the potential scale reduction factor on split chains (at ## convergence, Rhat=1).

这张照片有什么问题?

(A:收敛性差)(A:在一些链中,我们看到很多数值问题)

设置初始值

这个步骤通常是可选的,但对于某些模型可能是必需的。

在这种情况下,设置初始值可能很有用。回想一下我们转换参数的投影范围α吗?

默认情况下,Stan为-2到2之间的无约束尺度上的每个参数选择一个随机初始值。这个随机初始化是在无约束的支持为每一个参数。这保证了初始值与约束范围一致。

然而,当我们传入初始值时,这些是在制约规模。看到斯坦手册有关应用于约束变量的转换的更多细节。

gen_inits函数

让我们再次查看这个模型的参数块。

print_stan_file.(stan_file节='参数')
##参数{##真实alpha_raw;# #真正的μ;# #}

我们有两个应该设置初始值的参数。

让我们尝试修改初始范围alpha_raw使用比默认值更小的范围。

gen_inits < -功能() {列表(alpha_raw =0.01*rnorm(1),穆=rnorm(1))))}

我们将其封装在一个函数中,以便每个链将有不同的初始值集。

带有初始值的Stan代码

让我们尝试使用我们的初始值函数重新拟合我们的STAN模型。

recover_simulated2 < -rstan::斯坦(stan_filedata =gen_stan_data(simulated_data),链=4,iter =1000,init =gen_inits)打印(Recover_Simulated2)
## Stan模型的推理:weibull_surviv_null_model。## 4链,每个iter=1000;热身= 500;瘦= 1;每个链的热身后抽牌量=500,总抽牌量=2000# # # # # # alpha_raw意味着se_mean sd 2.5% 25% 50% 75% -0.02 0.00 0.00 -0.03 - -0.02 -0.02 - -0.01 # #μ-3.20 0.01 0.17 -3.55 -3.31 -3.20 -3.09 # #α0.84 0.00 0.04 0.76 0.81 0.84 - 0.87 # # lp__ -1400.41 0.07 1.17 -1403.77 -1400.84 -1400.03 -1399.58 # # 97.5% n_eff小红帽# # alpha_raw -2.89 224 1.00 -0.01 229 1.00 # #μα0.92 233 1.00 # # # # lp__-1399.33 311 1.01 ## ##使用NUTS(diag_e)于2016年6月22日22:20:45抽取样品。对于每个参数,n_eff是有效样本量的粗略度量,而Rhat是分裂链上的潜在规模缩减因子(在##收敛时,Rhat=1)。

现在我们看到更少的数值问题,更好的r帽值。

检查收敛性

评估融合可能是一个棘手的业务,因为每个模型和每个场景都不同。

一般来说,我在评估趋同时会考虑三件事:

  1. rhat值(它们接近1)?
  2. 回顾traceplots为lp__和关键参数
  3. 发射Shinystan.进一步检查。
回顾traceplots

在这种情况下,示踪图lp__(后圆木)看起来混合得很好:

rstan::traceplot(Recover_simulated2,“lp__”)

同样,我们感兴趣的参数看起来也不错:

rstan::traceplot(Recover_simulated2,c(“α”,“亩”),ncol =1)

发射shiny-stan

我们也可以发射Shinystan.检查链中的发散跃迁或过度自相关。这在诊断收敛问题时特别有用。

如果(互动()) shinystan::launch_shinystan(Recover_Simulated2)

回顾参数的后验分布

接下来,我们可以检查模型是否能够恢复我们的参数。

从Stanfit对象中提取参数

我们使用rstan:提取()函数从stanfit对象中提取参数。

如提取α&μ:

pp_alpha < -rstan::提炼(Recover_simulated2,“α”)$ alpha pp_mu < -rstan::提炼(Recover_simulated2,“亩”) $μ

每一个都是一个1xD向量的值,其中D =后验(热身后)的画数。

在本例中,我们有2000个:4个链* 1000次迭代/ 2

例如,我们可以画出后验分布α,在…的背景下test_alpha:

ggplot(data.frame(α=pp_alpha,穆=pp_mu)) +geom_density(aes(X =alpha))+geom_vline(aes(xintercept =test_alpha),颜色=“红色”) +ggtitle.(' '的后验分布\ n用红色显示真实值')

然后,重复同样的步骤μ:

ggplot(data.frame(α=pp_alpha,穆=pp_mu)) +geom_density(aes(X =μ))+geom_vline(aes(xintercept =test_mu),颜色=“红色”) +ggtitle.(mu的后验分布\ n用红色显示真实值')

参数的后验估计包含那些用来模拟我们的数据,但它们不一定是每个分布的模式。

而且,我们有高度的相关性μα:

ggplot(data.frame(α=pp_alpha,穆=pp_mu)) +geom_density2d(aes(X =α,y =μ))+geom_point.(aes(X =test_alpha,y =test_mu),颜色=“红色”,大小=2) +ggtitle.(和的后验分布\ n用红色显示真实的参数值')

这些值似乎在参数值的后验密度的边缘。

根据这个模型,参数值用于模拟我们的数据的可能性有多大?

从完全后验中取样的一个好处是我们可以通过对值进行汇总,直接计算后验概率。

要计算看到一个值为alpha >= 0.8的概率:

的意思是(pp_alpha > =test_alpha)
# # 0.841 [1]

μ(测试mu> = -3:

的意思是(pp_mu > =test_mu)
## [1] 0.12

然而,看到这种参数值组合的联合概率并不令人鼓舞:

的意思是(pp_mu > =test_mu&pp_alpha > =test_alpha)
# # 0.018 [1]

我们需要更多的迭代,以更精确地估计这一点。

后预测检查

接下来,我们可能会问,在恢复用于模拟数据的参数时出现的错误是否具有实质性。如果我们对生物标志物的推断是持续的,也许我们在估计基线危险参数时可能有点偏差?

为了做到这一点,我们将模拟来自我们的参数后测图的数据。这些被称为后预测值。它们的分布是后预测分布

(对模拟数据这样做可能看起来很疯狂,但这并不是真正的额外工作,因为我们可能想要在我们观察的数据集上重用这个过程。我们将小心翼翼地将每个步骤编写为一个函数,以便以后重用。)

模拟每次后拉的数据

我们可以用哈德利的Purrr :: Map2.为每对模拟数据μ*α值。

pp_newdata < -purrr::Map2.(.x =pp_alpha,.y =pp_mu,.f =~weibull_sim_data(α=以下方式,穆=.y,n =test_n))

如果你不熟悉purrr,我建议你检查结果对象。

我们现在有一个D数据集的列表,每个数据集包含一个根据绘制的参数值的模拟μ&α

让我们在后面的绘图中绘制事件发生的时间,并将其与用于拟合模型的测试数据集进行比较。

ggplot(pp_newdata % > %dplyr::bind_rows() % > %dplyr::变异(类型='后部预测值') % > %bind_rows(simulated_data%>%dplyr::变异(类型=实际数据的)),aes(X =os_months,组=os_status,颜色=os_status,填补=os_status))+geom_density(α=0.5) +facet_wrap(~类型,ncol =1)

这些看起来很相似。

总结后预测绘制

接下来,我们可能会询问生存曲线的后验估计。我们如何估计这一点?

一种方法(可能有几种)是:

  1. 计算每个观察时间点的累积生存率,每次提取后
  2. 将累积存活率汇总到离散单位
  3. 总结每一段时间的累积生存率。

这就是我们将在这里使用的方法。

##每个后部绘制的累积存活率Pp_survdata < -pp_newdata % > %purrr::地图(~dplyr::变异(。os_deceased =os_status = =“死者”) % > %purrr::地图(~生存::苏克福特(Surv(os_months,os_deced)〜1,data =) % > %purrr::地图(Fortify)##总结每个单位时间(月)的暨生存,总结为95%置信区间PP_Survdata_Agg < -pp_survdata % > %purrr::地图(~dplyr::变异(。time_group =地板上(时间)))%>%dplyr::bind_rows() % > %dplyr::group_by(time_group) % > %dplyr::总结(surv_mean =的意思是(SURV),surv_p50 =中位数(SURV),surv_lower =分位数(surv聚合氯化铝=0.025),surv_upper =分位数(surv聚合氯化铝=0.975) % > %dplyr::取消组()

最后,我们将生存曲线的后验预测模拟与原始测试数据集叠加。

测试数据测试数据test_data_kmcurve < -巩固(生存::苏克福特(Surv(os_months,os_deced)〜1,data =simulated_data%>%dplyr::变异(os_deceased =os_status = =“死者”))) % > %dplyr::变异(较低=SURV,上=SURV)ggplot(pp_survdata_agg%>%dplyr::变异(类型='后部预测值') % > %dplyr::改名(surv =surv_p50,较低=SURV_LOWER,上=surv_upper,时间=time_group)%>%bind_rows(test_data_kmcurve % > %dplyr::变异(类型=实际数据的)),aes(X =时间,组=类型,线型=类型))+geom_line(aes(y =SURV,颜色=类型))+geom_ribbon(aes(ymin =低,ymax =上),α=0.2) +XLIM.(c(0,200))

这里我们可以看到,原始(模拟)数据的生存曲线与后验预测密度非常吻合。这是一件好事,因为我们正在处理模拟数据!

保存为函数

与之前一样,我们希望将其包装在一个函数中,以便在以后的步骤中重用它,例如在处理TCGA数据时。

pp_predict_surv < -函数(pp_alpha pp_mu n,水平=0.9,情节=F,data =,sim_data_fun =pp_newdata <-purrr::Map2.(.x =pp_alpha,.y =pp_mu,.f =~sim_data_fun(α=以下方式,穆=.y,n =n))pp_survdata < -pp_newdata % > %purrr::地图(~dplyr::变异(。os_deceased =os_status = =“死者”) % > %purrr::地图(~生存::苏克福特(Surv(os_months,os_deced)〜1,data =) % > %purrr::地图(fortify) ##计算给定级别lower_p <-的分位数0+((1-级)/2) upper_p < -1-((1-级)/2)pp_survdata_agg < -pp_survdata % > %purrr::地图(~dplyr::变异(。time_group =地板上(时间)))%>%dplyr::bind_rows() % > %dplyr::group_by(time_group) % > %dplyr::总结(surv_mean =的意思是(SURV),surv_p50 =中位数(SURV),surv_lower =分位数(surv聚合氯化铝=下_P),surv_upper =分位数(surv聚合氯化铝=upper_p)) % > %dplyr::取消组() if (plot ==){返回(pp_survdata_agg)} ggplot_data < -pp_survdata_agg % > %dplyr::变异(类型='后部预测值') % > %dplyr::改名(surv =surv_p50,较低=SURV_LOWER,上=surv_upper,时间=如果(time_group) !is.null(数据))ggplot_data < -ggplot_data % > %bind_rows(巩固(生存::苏克福特(Surv(os_months,os_deced)〜1,data =数据% > %dplyr::变异(os_deceased =os_status = =“死者”))) % > %dplyr::变异(较低=SURV,上=SURV,类型=实际数据的))pl < -ggplot(ggplot_dataaes(X =时间,组=类型,线型=类型))+geom_line(aes(y =SURV,颜色=类型))+geom_ribbon(aes(ymin =低,ymax =上),α=0.2) pl}

将我们的模型与TCGA数据进行拟合

现在我们已经准备好将我们的模型与TCGA数据相匹配了。在这里,我们可以重用前面步骤中定义的几个函数。

wei_fit < -rstan::斯坦(文件=stan_file,data =gen_stan_data(clin_data),iter =1000,链=4,init =gen_inits)打印(wei_fit)
## Stan模型的推理:weibull_surviv_null_model。## 4链,每个iter=1000;热身= 500;瘦= 1;每个链的热身后抽牌量=500,总抽牌量=2000# # # #意味着se_mean sd # # alpha_raw 2.5% 25% 50% 75% 97.5% -0.01 0.00 0.01 -0.02 -0.01 - -0.01 -0.01 - 0.00 # # -3.78 0.01 0.20 -4.16 -3.91 -3.78 -3.64 -3.41μα# # 0.91 0.00 0.05 - 0.81 0.87 - 0.90 0.94 - 1.00 # # lp__ -915.62 0.03 0.92 - -918.02 -916.00 - -915.32 -914.95 - -914.71 # # n_eff小红帽# # # 1.01 # alpha_raw 364亩350 1.01 1.01α# # 365 # # lp__ 7001。00## ## Samples were drawn using NUTS(diag_e) at Wed Jun 22 22:21:19 2016. ## For each parameter, n_eff is a crude measure of effective sample size, ## and Rhat is the potential scale reduction factor on split chains (at ## convergence, Rhat=1).

检查收敛性

在前一节中,我们回顾了检查收敛性的各种方法。

检查traceplots

我们也看一下后对数的示踪图。我通常首先检查这个,因为它给我一个整体模型适合的感觉。

rstan::traceplot(Wei_fit,c(“lp__”),ncol =1)

而且,对于关键参数(在这种情况下,α&μ)

rstan::traceplot(Wei_fit,c(“α”,“亩”),ncol =1)

发射shinystan

对于更详细的模型检查,我们可以利用awesomeShinystan.。这将在易于使用的界面中显示最佳实践诊断(例如,链的自相关)。

如果(互动())launch_shinystan(wei_fit)

后预测检查

然后,我们使用之前定义的函数进行后验预测检查:

PL < -pp_predict_surv(pp_alpha =提炼(Wei_fit,“α”美元)α,pp_mu =提炼(Wei_fit,“亩”美元)亩,n =nrow(clin_data),data =clin_data,情节=t)pl +XLIM.(NA,150.) +ggtitle.(NULL威布尔模型的后验预测检验\ n符合TCGA数据;显示90%可信区间)

这与我们的数据非常吻合,尽管很难说它有多吻合。

在后验预测值的90% CI内观察到的事件率占时间点的比例是多少?

##总结每个区间pp_agg <-的预测事件率的90% CIpp_predict_surv(pp_alpha =提炼(Wei_fit,“α”美元)α,pp_mu =提炼(Wei_fit,“亩”美元)亩,n =nrow##将观察到的数据汇总到相同的time_groups中生存::苏克福特(Surv(os_months,(os_status = =“死者”)) ~1,data =Clin_data)%>%巩固() % > %dplyr::变异(time_group =地板上(时间))% > %dplyr::group_by(time_group) % > %dplyr::总结(observed_surv =的意思是(surv)) % > %dplyr::取消组() ##计算观察值在90% ci act_agg %>%内的比例dplyr::inner_join(pp_agg通过='time_group') % > %dplyr::变异(within_interval =ifelse(observed_surv > =surv_lower &观察到_surv <=surv_upper,1,0),time_set =减少(time_group打破了=c(0,One hundred.)))%>%dplyr::group_by(time_set) % > %dplyr::总结(的意思是(within_interval))
## Source: local data frame [2 x 2] ## ## time_set mean(within_interval) ##   ## 1 (0,100) 0.5641026 ## 2 NA 0.0000000

这并不是特别令人鼓舞,因为它表明,低于预期的90%的观察结果落在后验区间内。

但是,我们没有包含任何协变量值。如果有额外的协变量,情况会有所改善吗?