在进行Cox之前需要对数据进行假设检验,查看数据是否符合比例风险不变的前提
以下内容皆来自lifelines官网,https://lifelines.readthedocs.io/en/latest/
目的:检验数据集是否服从比例不变的假设?
注意:
在进行之前,应仔思考,是否需要进行比例风险假设?
不这样做的原因有很多:
1、如果您的目标是生存预测,那么您就不必关心比例风险。您的目标是最大化一些分数,与预测的生成方式无关。
2、如果样本量非常大,会出现较小的违反 比例假设的情况,没关系的
3、有合理的理由假设所有数据集都会违反比例风险假设。Stensrud&Hernán的“为什么要对比例危害进行测试?”中对此进行了详细说明
from lifelines.datasets import load_rossi
from lifelines import CoxPHFitter
rossi_dataset = load_rossi() #使用自带的数据
cph = CoxPHFitter() #建立比例风险Cox模型
cph.fit(rossi_dataset, duration_col='week', event_col='arrest') #模型拟合
cph.check_assumptions(rossi_dataset, p_value_threshold=0.05, show_plots=True) #进行假设检验
可以看到age ,wexp这两个变量不符合假设
由于wexp是二分类变量,我们可以通过 strata 的方式,将其分层,再计算
cph.fit(rossi_dataset, 'week', 'arrest', strata=['wexp'])
cph.print_summary(model="wexp in strata")
cph.check_assumptions(rossi_dataset, show_plots=True) #进行检验
可以看到wexp已经没有影响了,但age仍然违反比例风险假设,因此我们需要对age进行修改,使其符合比例风险模型的假设,有三种方法:
1、修改函数形式
如果协变量和对数风险之间的关联是非线性的,但模型仅进行线性拟合,可能会出现假阳性
因此,将变量变成非线性形式,看看是否符合,可以尝试选择添加二次项、三次项
from lifelines.datasets import load_rossi
rossi_higher_order_age = rossi_dataset.copy()
rossi_higher_order_age['age'] = rossi_higher_order_age['age'] - rossi_higher_order_age['age'].mean()
rossi_higher_order_age['age**2'] = (rossi_higher_order_age['age'] - rossi_higher_order_age['age'].mean())**2
rossi_higher_order_age['age**3'] = (rossi_higher_order_age['age'] - rossi_higher_order_age['age'].mean())**3
cph.fit(rossi_higher_order_age, 'week', 'arrest', strata=['wexp'])
cph.print_summary(model="quad and cubic age terms"); print()
cph.check_assumptions(rossi_higher_order_age, show_plots=True, p_value_threshold=0.05)
可以看到,二项式或三项式符合模型的假设
2、对变量进行分层
将变量为大小相等的分类
如果分层过大,缺点是丢失大多数信息(因为将不同的值合并在一类)
因此,最佳分层数值介于两者之间
from lifelines.datasets import load_rossi
import pandas as pd
rossi_strata_age = rossi_dataset.copy()
rossi_strata_age['age_strata'] = pd.cut(rossi_strata_age['age'], np.arange(0, 80, 3))
#生成一个0-80,间隔为3的队列,把年龄按照其所处的位置进行划分
rossi_strata_age[['age', 'age_strata']].head()
# 删除原始的age的列
rossi_strata_age = rossi_strata_age.drop('age', axis=1)
cph.fit(rossi_strata_age, 'week', 'arrest', strata=['age_strata', 'wexp'])
cph.print_summary(3, model="stratified age and wexp")
cph.check_assumptions(rossi_strata_age)
提示现在数据符合比例风险模型的假设
3、引入随时间变化的协变量
分两个步骤完成:
第一步,是将数据集转换为episodic格式。这意味着我们将变量从单行拆分为n新行,每个新行代表该变量的某个时间段。
from lifelines.utils import to_episodic_format
rossi_dataset = load_rossi() #导入数据
rossi_long = to_episodic_format(rossi_dataset, duration_col='week', event_col='arrest', time_gaps=1.)
#转化数据格式,time_gap参数指定周期大小
rossi_dataset.head(25)
rossi_long.head(25)
发现每个样本都多了一个新的ID列(可以在数据框中自己指定该ID)。
该ID用于跟踪一段时间内的样本。请注意,arrest在(可能)事件发生之前的所有期间,col均为0
第二步,是在age和stop之间创建一个交互项。这是随时间变化的变量。
因为我们正在处理episodic格式。必须使用CoxTimeVaryingFitter代替CoxPHFitter,
rossi_long['time*age'] = rossi_long['age'] * rossi_long['stop']
from lifelines import CoxTimeVaryingFitter
ctv = CoxTimeVaryingFitter()
ctv.fit(rossi_long,
id_col='id',
event_col='arrest',
start_col='start',
stop_col='stop',
strata=['wexp'])
ctv.print_summary(3, model="age * time interaction")
ctv.plot()
结论:
使用3种方法中,任何一种方法,点估计和标准误差都非常接近,因此使用任何一种方法都可以