python生存分析入门

引入

什么是生存分析

生存分析是对生存资料的分析。所谓生存资料是指描述寿命或者一个发生时间的数据。通过对某一具有相同特质的群体进行数据分析,我们可以得到这类人活过一定时间的概率。这就叫做生存分析。一个人的生存时间长短与许多因素有关,研究各个因素与生存时间有无关系以及关联程度大小,这也是生存分析。推广来说,疾病的复发可以看做“健康期”的生存,机器的故障可以看做“机器”的生存,甚至下岗职工再就业可以看做“失业期”的生存,生存分析可以被推广至很多问题的分析中去。

应用场景

生存分析最早在生物医学中使用的最多,用来预估某个群体的存活时间,后来被推广到了更广泛的领域。一个做生物的专家可能通常会关心这样一个问题:这个群体的样本能活多久?这个问题我们通常会使用生存分析来回答。 这个群体可以理解为某个国家的人民,或者注射过某种药物的一些病人。

同样也可以推广到更一般的场景,公司的客户流失情况,一个用户开始接受他们的服务可以认为是一个样本的出生,一个客户的离开可以认为是一个样本的死亡。用户流失情况分析等问题很多电商平台也十分关注。

数据截断

以样本的出生和死亡为例,我们观测一组样本的生存时间。然而我们观测和追踪他们是需要时间和金钱的成本的,不可能因为样本一直存活着,我们就一直将某个实验做下去。通常,专家只会对一组样本观测一段时间。

同样也存在左侧截断和中间截断

一个常见的错误是人们可能在评估目标群体存活时间时会简单的忽略右侧截断
考虑一种情况,其中人口实际上由两个亚群组成, 𝐴 和 𝐵。人口𝐴 寿命非常短,例如平均2个月,人口 𝐵 享有更长的使用寿命,例如平均12个月。我们事先不知道这种区别。假定我们的实验进行了十个月,即在𝑡=10就结束了,我们希望调查整个人口的平均寿命。

在下图中,红线表示观察到死亡事件的个体的寿命,蓝线表示右删失个体的寿命(未观察到死亡)。如果要求我们估计人口的平均寿命,而我们天真地决定不包括经过权利审查的个人,那么很明显,我们将严重低估真实的平均寿命。

from lifelines.plotting import plot_lifetimes
import numpy as np
from numpy.random import uniform, exponential

N = 25

CURRENT_TIME = 10

actual_lifetimes = np.array([
    exponential(12) if (uniform() < 0.5) else exponential(2) for i in range(N)
])
observed_lifetimes = np.minimum(actual_lifetimes, CURRENT_TIME)
death_observed = actual_lifetimes < CURRENT_TIME

ax = plot_lifetimes(observed_lifetimes, event_observed=death_observed)

ax.set_xlim(0, 25)
ax.vlines(10, 0, 30, lw=2, linestyles='--')
ax.set_xlabel("time")
ax.set_title("Births and deaths of our population, at $t=10$")
print("Observed lifetimes at time %d:\n" % (CURRENT_TIME), observed_lifetimes)

上面我们设定了最大值等于CURRENT_TIME=10,所以我们直观查了10个月的数据,后面没有发生结束事件的我们并不知道其真实的存活时长。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FymHigLR-1591174155281)(evernotecid://DD492144-9AFF-43C1-9BC0-5A625709FC62/appyinxiangcom/28357599/ENResource/p9)]

而真实的数据其实是下面这样的

ax = plot_lifetimes(actual_lifetimes, event_observed=death_observed)
ax.vlines(10, 0, 30, lw=2, linestyles='--')
ax.set_xlim(0, 25)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EHJP6DDF-1591174155283)(evernotecid://DD492144-9AFF-43C1-9BC0-5A625709FC62/appyinxiangcom/28357599/ENResource/p10)]

如果我们不删除发生右侧截断的样本而直接求平均值,仍然会低估这个群体的平均生存时间,要注意的是,我们只能观测到10时刻之前的事情,10时刻之后的我们并不能看到。

生存分析最初被设计出来的目的就是为了处理存在右侧截断的这种数据的。但是,即使我们的数据不包含右侧截断,生存分析仍然是一个强大有效的工具。

上述例子是为了阐述方便所以让所有的样本的出生从0时刻开始,而实际上这是不必要的,生存分析并不要求所有样本在同一时刻出生,它只关注每个样本从出生到death触发或到实验结束之间的间隔时间,每个样本随时可以出生。

基本对象

接下来,我们介绍生存分析中的三个基本对象: 生存函数,风险函数和累积风险函数。

生存函数

设𝑇取自研究人群的(可能是无限的,但总是非负的)随机寿命。例如,一对夫妇结婚的时间。或用户进入网页所花费的时间(如果从不输入,则为无限时间)。生存函数S(t)S_{(t)}的被定义为:
S(t)=Pr(T>t)S_{(t)}=Pr(T>t)
解释:S(t)S_(t)描述了这个群体的样本生存时间大于t的概率,换句话说,直到时刻t,我们仍然没有观测到death事件的发生。

它有以下几个性质:

  1. 0S(t)10\leq S_{(t)} \leq 1
  2. FT(t)=1S(t)F_T(t)=1- S_{(t)}FT(t)F_T(t)是随机变量T的累积分布函数。
  3. S(t)S_{(t)}是一个单调不增的函数。

一个生存函数的示例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ETPBkgZT-1591174155285)(evernotecid://DD492144-9AFF-43C1-9BC0-5A625709FC62/appyinxiangcom/28357599/ENNote/p27?hash=fedd878e1c19ce47cf1c9f91a9617b18)]

风险函数

我们同样可能会关心某个样本在t时刻死亡的概率,也就是说,直到t时刻我们都没有观察到该样本的死亡,而在t时刻的一个右邻域观察到该样本死亡。即:
limδt0Pr(tTt+δtT>t)\lim_{\delta_t \to 0} Pr(t\leq T \leq t+\delta_t|T>t)
由于上述极限随着δt\delta_t的减小而趋于0,我们定义
h(t)=limδt0Pr(tTt+δtT>t)δth{(t)=}\lim_{\delta_t \to 0}\frac{Pr(t\leq T \leq t+\delta_t|T>t)}{\delta_t}

可以证明这等于:
h(t)=S(t)S(t)h{(t)}=\frac{-S_{(t)}^{'}}{S_{(t)}}

可以运用贝叶斯公式

分离变量后,两遍同时取积分,并注意到S(0)=1S(0)=1
即可以得到S(t)=exp(oth(x)dx)S(t)=exp(-\int_o^th(x)dx)

累计风险函数

H(x)=oth(x)dxH(x)=\int_o^th(x)dx
那么S(t)S(t)又可以写为S(t)=exp(H(x))S(t)=exp(-H(x))

上面的方程式,定义了所有生存函数。请注意,我们现在可以谈论生存功能𝑆(𝑡)或累积风险函数𝐻(𝑡),我们可以轻松地来回转换。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X4Y1CGdQ-1591174155286)(evernotecid://DD492144-9AFF-43C1-9BC0-5A625709FC62/appyinxiangcom/28357599/ENNote/p27?hash=1f523a369eba384a36fa5b883701be31)]

下面两个图表表示上图中风险函数和累积风险。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TnKxuzZI-1591174155288)(evernotecid://DD492144-9AFF-43C1-9BC0-5A625709FC62/appyinxiangcom/28357599/ENNote/p27?hash=24aec27f4042ea1ac5d010eb305adb30)]

估计生存函数

这里以Kaplan-Meier为例:

Kaplan-Meier是一种非参数的估计方法。

简单说一下非参数估计和参数估计
参数估计:我们通过一定的基本假设和建模获得了待估计函数的形式,而有若干控制该函数具体表现的参数。而我们的目的是从形式已知参数未知的模型簇里找出合适的参数。把这个函数当做我们对目标函数的估计。
非参数估计:我们并不对待估计的函数形式做任何假设,而是直接从数据出发去估计它。
优缺点:在数据量较小的时候,参数估计的表现会比较好。而非参数估计会由于数据量有限而无法表现出良好的性能。在数据量充足的时候,非参数估计由于减少了强加给数据的假设而会表现的比参数估计好。通常非参数估计的计算量大于参数估计。

单因素生存分析方法,可用生存率的估计、生存率比较及较影响因素分析。倾向于给与某种治疗措施后生存时间的变化情况。大小样本均适用,除比较因素外要求其他混杂因素组间均衡。当用分层变量控制混杂因素时,分层因素只限一个,且须是分类变量.
S(t)^=nidini\hat{S_{(t)}} = \prod{\frac{n_i-d_i}{n_i}}

did_i是在该时间点t实际死亡的人数,nin_i是在该时间点t有死亡风险的人数

数据演示
看下数据集

from lifelines.datasets import load_dd

data = load_dd()
data.info()

<class ‘pandas.core.frame.DataFrame’>
RangeIndex: 1808 entries, 0 to 1807
Data columns (total 12 columns):

Column Non-Null Count Dtype
0 ctryname 1808 non-null object
1 cowcode2 1808 non-null int64
2 politycode 1801 non-null float64
3 un_region_name 1808 non-null object
4 un_continent_name 1808 non-null object
5 ehead 1808 non-null object
6 leaderspellreg 1808 non-null object
7 democracy 1808 non-null object
8 regime 1808 non-null object
9 start_year 1808 non-null int64
10 duration 1808 non-null int64
11 observed 1808 non-null int64

dtypes: float64(1), int64(4), object(7)
memory usage: 169.6+ KB

再lifelines包中,我们需要KaplanMeierFitter来进行训练

from lifelines import KaplanMeierFitter
kmf = KaplanMeierFitter()

为了进行此估算,我们需要每个领导人的上任时间/是否在职,以及是否被观察到他们离开了办公室(在2008年去世或就职的领导者,该数据记录的最新日期,没有观察到死亡事件)

T = data["duration"]
E = data["observed"]

kmf.fit(T, event_observed=E,label='KM-estimate')
kmf.plot()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qNMYL6Kq-1591174155289)(evernotecid://DD492144-9AFF-43C1-9BC0-5A625709FC62/appyinxiangcom/28357599/ENNote/p27?hash=9ca1a0cdc253f7ceadb655c32535fcbe)]

y轴表示领袖在t年后仍然在位的可能性,x轴为t(年)
我们看到很少有领导者能够任职20年以上。

kmf.median_survival_time_
4.0

中位数定义了平均50%的人口已经不在职的时间点
这意味着,在全球范围内,当选领导人在四年或更短的时间内有50%的机会被停职!要获得中位数的置信区间,可以使用:

from lifelines.utils import median_survival_times
median_ci = median_survival_times(kmf.confidence_interval_)

我们对民主政权与非民主政权进行细分

ax = plt.subplot(111)

dem = (data["democracy"] == "Democracy")
#民主政权
kmf.fit(T[dem], event_observed=E[dem], label="Democratic Regimes")
kmf.plot(ax=ax)
#非民主政权
kmf.fit(T[~dem], event_observed=E[~dem], label="Non-democratic Regimes")
kmf.plot(ax=ax)

plt.ylim(0, 1);
plt.title("Lifespans of different global regimes");

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RuCKXFdL-1591174155290)(evernotecid://DD492144-9AFF-43C1-9BC0-5A625709FC62/appyinxiangcom/28357599/ENNote/p27?hash=80f62a58be08ad9bbbb1393de1d4b338)]

这些非民主政权的存在时间长了令人难以置信。民主政权确实对死亡有一种自然偏见:通过选举和自然限制(美国规定了严格的八年限制)。非民主国家的中位数只有民主政权的两倍左右,但差别却显而易见:如果您是非民主国家的领导人,并且已经超过10年大关,可能还有很长的路要走。同时,民主领袖很少能做到十年,而在那之后的寿命很短。

在这里,生存功能之间的差异非常明显,并且进行统计检验似乎很花哨。如果曲线更相似,或者我们拥有的数据更少,我们可能会对执行统计检验感兴趣。在这种情况下,生命线包含lifelines.statistics用于比较两个生存功能的例程 。下面我们演示这个例程。该功能lifelines.statistics.logrank_test()是生存分析中常用的统计测试,用于比较两个事件序列的生成器。如果返回的值超过某个预先指定的值,则我们认为该系列具有不同的生成器。

Nelson Aslen估计

累计风险函数–估计值的总和比逐点估计的稳定得多

H(t)^=titdini\hat{H_{(t)}} = \sum_{t_i \leq t}{\frac{d_i}{n_i}}
did_i是在时刻tit_i死亡的数量,nin_i是有潜在死亡的个人的数量

the number of susceptible individuals

T = data["duration"]
E = data["observed"]

from lifelines import NelsonAalenFitter
naf = NelsonAalenFitter()

naf.fit(T,event_observed=E)

print(naf.cumulative_hazard_.head())
naf.plot()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5ZpIuFFk-1591174155292)(evernotecid://DD492144-9AFF-43C1-9BC0-5A625709FC62/appyinxiangcom/28357599/ENNote/p27?hash=b8e15fd80ceb16bf4c53e0890a1250ef)]
该曲线的变化率是风险函数的估计

naf.fit(T[dem], event_observed=E[dem], label="Democratic Regimes")
ax = naf.plot(loc=slice(0, 20))

naf.fit(T[~dem], event_observed=E[~dem], label="Non-democratic Regimes")
naf.plot(ax=ax, loc=slice(0, 20))

plt.title("Cumulative hazard function of different global regimes");

我们对民主政权与非民主政权进行细分
在这里插入图片描述
从变化的速度来看,尽管民主政权的持续风险性要高得多,但两种政权都具有持续的风险性。

COX分析

定义

生存回归 -顾名思义,我们将协变量(例如年龄,国家/地区等)与另一个变量进行回归-在这种情况下为持续时间。
生存回归中有一些流行的模型:Cox模型,加速故障模型和Aalen加法模型

在进行Cox回归分析前,如果样本不多而变量较多,建议先通过单变量分析(KM法绘制生存曲线、Logrank检验等)考察所有自变量与因变量之间的关系,筛掉一些可能无意义的变量,再进行多因素分析,这样可以保证结果更加可靠。即使样本足够大,也不建议把所有的变量放入方程直接分析,一定要先弄清楚各个变量之间的相互关系,确定自变量进入方程的形式,这样才能有效的进行分析。
单因素分析后,应当考虑应该将哪些自变量纳入Cox回归模型。一般情况下,建议纳入的变量有:1)单因素分析差异有统计学意义的变量(此时,最好将P值放宽一些,比如0.1或0.15等,避免漏掉一些重要因素);2)单因素分析时,没有发现差异有统计学意义,但是临床上认为与因变量关系密切的自变量。

Cox模型的基本假设为
在任意一个时间点,两组人群发生时间的风险比例是恒定的;或者说其危险曲线应该是成比例而且是不能交叉的;也就是如果一个体在某个时间点的死亡风险是另外一个体的两倍,那么在其他任意时间点的死亡风险也同样是2倍。

h(tx)hazard=b0(t)baselinehazardexp(i=1nbi(xixi))logpartialhazardpartialhazard\underbrace{h(t|x)}_{hazard}=\overbrace{b_0(t)}^{baseline \quad hazard}\quad\underbrace{exp\overbrace{(\sum_{i=1}^nb_i(x_i-\overline{x_i}))}^{log-partial \quad hazard}}_{partial\quad hazard}

b0(t)b_0(t)称为基准危险率。在上述产品中,部分危害是随时间变化的标量因子,只会增加或减少基线危害。因此,协变量的变化只会增加或减少基线风险。

数据介绍

用一个具体的数据演示一下
生存回归所需的数据集必须采用Pandas DataFrame格式。DataFrame的每一行都应该是一个观察值。应该有一栏表示观察的持续时间。可能会有一栏表示每个观察的事件状态(如果发生事件,则为1,如果截断,则为0)。还希望对其他协变量进行回归。
使用的示例数据集是Rossi累犯数据集

from lifelines.datasets import load_rossi

rossi = load_rossi()

"""
     week  arrest  fin  age  race  wexp  mar  paro  prio
0      20       1    0   27     1     0    0     1     3
1      17       1    0   18     1     0    0     1     8
2      25       1    0   19     0     1    0     1    13
3      52       0    1   23     1     1    1     1     1
"""

DataFrame rossi包含432个观察值。该week列是持续时间,arrest列事件发生,而另一列代表我们要对回归变量。

统计分析
from lifelines import CoxPHFitter
from lifelines.datasets import load_rossi

rossi_dataset = load_rossi()

cph = CoxPHFitter()
cph.fit(rossi_dataset, duration_col='week', event_col='arrest')

cph.print_summary() 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jz3TooCW-1591174155296)(evernotecid://DD492144-9AFF-43C1-9BC0-5A625709FC62/appyinxiangcom/28357599/ENResource/p29)]
z值代表Wald统计量,其值等于回归系数coef除以其标准误se(coef),即z = coef/se(coef);有统计量必有其对应的假设检验的显著性P值,其说明coef是否与0有统计学意义上的显著差别。
例子
prio(先前的逮捕次数)系数约为0.09。因此,一个单位的增加prio意味着基准危害将增加一个系数。exp(0.09)=1.10大约增加10%。回想一下,在Cox比例风险模型中,较高的风险意味着事件发生的风险更大。价值exp(0.09)称为危险比,这个名称在另一个示例中将很明显。
考虑系数mar(对象是否已婚)。列中的值为二进制:0或1,表示未婚或已婚。与mar,相关的系数值exp(−.43),是与结婚相关的危险比的值,即:

exp(0.43)=hazardofmarriedsubjectsattimethazardofunmarriedsubjectsattimetexp(-0.43)= \frac{hazard\quad of\quad married\quad subjects\quad at\quad time\quad t}{hazard\quad of\quad unmarried\quad subjects\quad at\quad time\quad t}

请注意,左侧是一个常数(具体来说,它与时间无关, 𝑡),但右侧有两个因素可能会随时间而变化。该比例风险的假设是这种关系是真实的。也就是说,危险可以随时间变化,但是它们之间的比率保持不变。稍后我们将处理检查此假设。但是,实际上,危险比在研究期间会发生变化是非常普遍的。然后,危险比可以解释特定时期的危险比的某种加权平均值。结果,危险比可能严重取决于随访的持续时间。

系数绘制
cph.plot()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uinA8IPi-1591174155297)(evernotecid://DD492144-9AFF-43C1-9BC0-5A625709FC62/appyinxiangcom/28357599/ENNote/p27?hash=5d9c9873cb288c32d27cd58cb4444ded)]

绘制改变协变量的效果
cph.plot_covariate_groups('prio', [0, 2, 4, 6, 8, 10], cmap='coolwarm')

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uHB50IWj-1591174155299)(evernotecid://DD492144-9AFF-43C1-9BC0-5A625709FC62/appyinxiangcom/28357599/ENNote/p27?hash=7182f9a11b9d01efc5c852f204552033)]

github
参考:
https://wenku.baidu.com/view/13b07ba53169a4517623a378.html
https://zhuanlan.zhihu.com/p/34802509
https://lifelines.readthedocs.io/en/latest/Survival%20analysis%20with%20lifelines.html
https://segmentfault.com/a/1190000015895321
https://www.jianshu.com/p/e80eb4168043

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章