医学研究中的生存数据建模

21.1 生存函数

\[ S(t)=P(T>t)=1-F(t)=\int_{t}^{+\infty}f(x)dx \]

其中S(t)是累计生存概率或生存率,量化了生存时间大于t的概率。f(x)是密度函数,呈右偏态分布,反映了任意时间点 t 终点事件的瞬时发生率。F(t)=P(T<t)是f(t)在区间[0,t]的累计形式,也称为分布函数或累积函数。

21.2 风险函数

\[ \begin{aligned} h(t)=&\lim_{\Delta t\to 0}\frac{P(t\le T<t+\Delta t|T\ge t)}{\Delta t}\\ =&\lim_{\Delta t\to 0}\frac{P(t\le T<t+\Delta t\ \&\ T\ge t)}{\Delta t·P(T\ge t) }\\ =&\lim_{\Delta t\to 0}\frac{S(t)- S(t+\Delta t)}{\Delta t·S(t)}\\ =&-\frac{d(\ln S(t))}{dt} \end{aligned} \]

\[S(t)=e^{-\int_0^t h(u)du} \]

\[ \begin{aligned} h(t)\Delta t=&P(t\le T<t+\Delta t|T\ge t) =\frac{P(t\le T<t+\Delta t\ \&\ T\ge t)}{P(T\ge t) }\\ =&\frac{P(t\le T<t+\Delta t)}{P(T\ge t) }\\ =&\frac{f(t)\Delta t}{S(t)} \end{aligned} \]

\(f(t)=h(t)S(t)\)

21.3 乘积极限法

product limit method 也称为Kaplan-Meier 法。

\(t_1<t_2<t_3<...<t_n\),样本量大小n,ti 代表个体i发生终点事件或右删失的时间。由于一些个体有相同的生存时间,它们被称为 tied 观测时间,生存时间的个数小于样本量n。

21.3.1 点估计S(t)

\(n_1>n_2>n_3>...>n_n\) ,ni d代表在时间点ti 暴露于特定事件风险的幸存者数量。

\(d_i\) 代表在时间点ti 发生终点事件的数量。(如果没有 tie,di=1或0)

生存率的KM估计计算公式:

\[ \hat S(t)=\prod_{t_i\le t}\frac{n_i-d_i}{n_i} \tag{21.1}\]

Equation 21.1 包括了删失情况,如果从ti-1 到ti 发生了删失,但没有终点事件,di =0,条件概率等于1。

21.3.2 区间估计S(t)

(1-α)×100% CI \([\hat S(t)-z_{1-\alpha/2}\sqrt{Var\left [\hat S(t) \right]},\hat S(t)+z_{1-\alpha/2}\sqrt{Var\left [\hat S(t) \right]}]\)

其中\(Var\left [\hat S(t) \right]=\hat S(t)^2\sum_{t_i\le t}\frac{d_i}{n_i(n_i-d_i)}\) (Greenwood method )

Code
df_raw <- tibble(
    id=1:5,
    sex=factor(c("Male","Male","Female","Male","Female")),
    age_years=c(53,62,75,73,61),
    outcome=c("Loss to follow-up","Death","Death","Relapse","Survival"),
    time=c("37.5+",44.3,25.3,18.1,"56.7+")
)
df <- df_raw |> arrange(time) |> select(id,outcome,time) |> 
    mutate(
        d_i=if_else(outcome=="Relapse"|outcome=="Death",1,0),
        time=parse_number(time),
    )
df
id outcome time d_i
4 Relapse 18.1 1
3 Death 25.3 1
1 Loss to follow-up 37.5 0
2 Death 44.3 1
5 Survival 56.7 0
Code

library(survminer)
library(survival)

km_fit<-survfit(Surv(time,d_i)~1,data=df)
# t_i
km_fit$time
#> [1] 18.1 25.3 37.5 44.3 56.7
# d_i
km_fit$n.event
#> [1] 1 1 0 1 0
# cnesored
km_fit$n.censor
#> [1] 0 0 1 0 1
# n_i
km_fit$n.risk
#> [1] 5 4 3 2 1

# 生存率
km_fit$surv
#> [1] 0.8 0.6 0.6 0.3 0.3

summary(km_fit)
#> Call: survfit(formula = Surv(time, d_i) ~ 1, data = df)
#> 
#>  time n.risk n.event survival std.err lower 95% CI upper 95% CI
#>  18.1      5       1      0.8   0.179       0.5161            1
#>  25.3      4       1      0.6   0.219       0.2933            1
#>  44.3      2       1      0.3   0.239       0.0631            1

21.4 单因素分组生存曲线的比较

检验方法 权重
log-rank test 1
Wilcoxon test nj
Tarone-Ware test \(\sqrt{n_j}\)
Peto test \(\hat S(t_j)\)

\[ \chi^2=\frac{\left(\sum_jw(t_j)(m_{ij}-e_{ij})\right)^2}{\hat {Var}\left(\sum_jw(t_j)(m_{ij}-e_{ij})\right)} \]

21.4.1 log-rank test

  1. H0 :两总体的生存曲线是相同的。

  2. 计算当第j次发生终点事件各组终点事件的期望值(e1j ,e2j

    \(e_{1j}=\left ( \frac{n_{1j}}{n_{1j}+n_{2j}}\right)\times (m_{1j}+m_{2j})\)

    \(e_{2j}=\left ( \frac{n_{2j}}{n_{1j}+n_{2j}}\right)\times (m_{1j}+m_{2j})\)

    其中mij 表示在第 j 个时间点第 i 组终点事件的数量,nij 表示在第 j 个时间点第 i 组初始观测的数量

  3. 对所有时间点对终点事件的观测值和期望值的差异求和

    \(O_i-Ei=\sum_j(m_{ij}-e_{ij})\ \ \ (i=1,2)\)

    计算其方差估计值

    \(\hat{Var}=\sum_j\frac{n_{1j}n_{2j}(m_{1j}+m_{2j})(n_{1j}+n_{2j}-m_{1j}-m_{2j})}{(n_{1j}+n_{2j})^2(n_{1j}+n_{2j}-1)}\ \ \ (i=1,2)\)

  4. 计算log-rank test 的检验统计量

    \[ \chi^2=\frac{(O_1-E_1)^2}{\hat{Var}(O_1-E_1)} \ 或者 \ \chi^2=\frac{(O_2-E_2)^2}{\hat{Var}(O_2-E_2)} \]

    也可以近似估计为

    \[ \chi^2=\sum_{i=1}^2\frac{(O_i-E_i)^2}{E_i} \sim \chi^2(\nu=1) \]

Code
df <- read_csv("data/log-rank-survival.csv")

# 使用 Kaplan-Meier 方法创建一个Surv对象
surv_formula <- Surv(Days,status)~treatment

km<-survfit(surv_formula,data=df)
summary(km)
#> Call: survfit(formula = surv_formula, data = df)
#> 
#>                 treatment=CON 
#>  time n.risk n.event survival std.err lower 95% CI upper 95% CI
#>    11     14       1   0.9286  0.0688       0.8030        1.000
#>    13     13       1   0.8571  0.0935       0.6921        1.000
#>    14     12       2   0.7143  0.1207       0.5129        0.995
#>    15     10       1   0.6429  0.1281       0.4351        0.950
#>    17      9       3   0.4286  0.1323       0.2341        0.785
#>    20      6       2   0.2857  0.1207       0.1248        0.654
#>    21      4       2   0.1429  0.0935       0.0396        0.515
#>    25      2       1   0.0714  0.0688       0.0108        0.472
#>    27      1       1   0.0000     NaN           NA           NA
#> 
#>                 treatment=DPVB 
#>  time n.risk n.event survival std.err lower 95% CI upper 95% CI
#>    20     14       1    0.929  0.0688        0.803        1.000
#>    23     13       1    0.857  0.0935        0.692        1.000
#>    27     12       1    0.786  0.1097        0.598        1.000
#>    28     11       1    0.714  0.1207        0.513        0.995
#>    30     10       1    0.643  0.1281        0.435        0.950
#>    32      9       1    0.571  0.1323        0.363        0.899
#>    38      8       1    0.500  0.1336        0.296        0.844
#>    39      7       1    0.429  0.1323        0.234        0.785
#>    45      6       1    0.357  0.1281        0.177        0.721
#> 
#>                 treatment=LDRT 
#>  time n.risk n.event survival std.err lower 95% CI upper 95% CI
#>    13     14       2   0.8571  0.0935       0.6921        1.000
#>    15     12       1   0.7857  0.1097       0.5977        1.000
#>    16     11       1   0.7143  0.1207       0.5129        0.995
#>    18     10       1   0.6429  0.1281       0.4351        0.950
#>    19      9       2   0.5000  0.1336       0.2961        0.844
#>    20      7       3   0.2857  0.1207       0.1248        0.654
#>    24      4       1   0.2143  0.1097       0.0786        0.584
#>    25      3       1   0.1429  0.0935       0.0396        0.515
#>    27      2       1   0.0714  0.0688       0.0108        0.472
#>    30      1       1   0.0000     NaN           NA           NA
#> 
#>                 treatment=LR_DPVB 
#>  time n.risk n.event survival std.err lower 95% CI upper 95% CI
#>    30     14       1    0.929  0.0688        0.803            1
#>    40     13       1    0.857  0.0935        0.692            1

# 执行Log-rank检验
logrank_test <- survdiff(surv_formula,data = df,subset = T,na.action = "na.omit")
logrank_test$chisq
#> [1] 58.43627
logrank_test$pvalue
#> [1] 1.268397e-12