用lifelines画正确kaplan-meier曲线需:传入int型event_observed和durations;设label命名曲线;ci_show=false关闭置信带;plt.ylim(0,1.05)防截断;多组需统一timeline并单独logrank_test;导出数据用survival_function_、median_survival_time_等属性。

怎么用 lifelines 画出正确的 Kaplan-Meier 曲线
直接调 KaplanMeierFitter().fit() 再 plot() 是最简路径,但默认不显示风险表、没处理右删失标记、也没分组对比——这些都会让图在临床或汇报场景里站不住脚。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 必须传入两个 Series:
durations(生存时间)和event_observed(是否发生事件,1=事件发生,0=删失),顺序错或类型是 bool 而非 int 容易报ValueError: Input contains NaN - 用
label参数明确命名每条曲线,别依赖默认的KM_estimate - 加
ci_show=False关闭置信带——它默认开启,但小样本下带子太宽,反而干扰判断 - 绘图后立刻调
plt.ylim(0, 1.05),避免 y 轴自动截断导致终点看起来“突降”
from lifelines import KaplanMeierFitter kmf = KaplanMeierFitter() kmf.fit(durations=df['time'], event_observed=df['death'], label='High-risk') kmf.plot(ci_show=False)
多组生存曲线怎么对齐时间轴并加 log-rank 检验
不同组的 durations 长度不一致、最大时间点不同,直接叠图画出来会错位;log-rank p 值不显著,常是因为没把分组变量转成整数编码,或删失比例差异太大。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 用
groupby分组后分别fit(),别拼成一个大数组再切片——lifelines 不吃这种“伪分组” - 所有组共用同一个
timeline:先用np.arange(0, df['time'].max()+1)生成统一横轴,再传给每个fit(timeline=...) - log-rank 检验必须用
multivariate_test或logrank_test单独跑,fit()不自动算 p 值 - 检验前检查各组删失率:若某组删失 >70%,log-rank 敏感度暴跌,得改用 restricted mean survival time(RMST)
from lifelines.statistics import logrank_test
results = logrank_test(df['time'][df['group']==0],
df['time'][df['group']==1],
df['death'][df['group']==0],
df['death'][df['group']==1])
怎么导出 KM 表数据(不是图)用于论文表格
期刊常要求提供“中位生存时间 + 95% CI”、“1年/3年生存率”等结构化数字,但 plot() 只输出图像,不返回表格。
实操建议:
立即学习“Python免费学习笔记(深入)”;
-
kmf.survival_function_是 DataFrame,索引是时间点,列是估计的 S(t),直接.loc[12]可取 12 个月生存率(单位要和原始数据一致) - 中位生存时间用
kmf.median_survival_time_,但它可能为None(曲线没降到 0.5 以下),得加try/except或 fallback 到kmf.percentile(0.5) - 95% CI 对应的
kmf.confidence_interval_是同结构 DataFrame,注意它和survival_function_行索引严格对齐 - 别用
print(kmf.summary)——那只是格式化文本,不好提取数值
为什么 plot 出来曲线末端突然垂直掉到 0?
这不是 bug,是 lifelines 默认启用 “episodic” 终止逻辑:最后一个观测事件点之后,S(t) 强制归零。真实场景中,只要还有删失样本,就不该这么画。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 加参数
at_risk_counts=False并不解决这个问题,真正开关是kmf.fit(..., entry=None)中的entry——但更常用的是手动截断:用kmf.survival_function_.iloc[:-1]删掉最后一行再重绘 - 更稳妥的做法是改用
estimate_cumulative_density()反推,或切换到scikit-survival的KaplanMeier,它默认保持末段水平 - 如果数据里存在
time=0的记录,且event_observed=1,也会触发异常跳变——得提前df = df[df['time'] > 0]










