one of a kind scene

[jupyter] Survival analysis 본문

Python

[jupyter] Survival analysis

specialscene 2020. 3. 10. 13:02
Survival Analysis
In [8]:
# !pip install pandas
# !pip install lifelines
In [36]:
import pandas as pd
# Survival Function 추정 부분
from lifelines import KaplanMeierFitter
# Culmulative hazard function 추정 부분
from lifelines import NelsonAalenFitter
# 유의성 검증에 활용되는 부분
from lifelines.statistics import logrank_test

데이터 입력

  • time : 시간 의미(day 기준)
  • event : 이탈여부 의미
In [11]:
data = pd.DataFrame(
    {
        'time': [1, 4, 7, 12, 14, 20, 26, 40, 45],
        'event': [True, True, True, False, True, False, True, False, False]
    },
    index = ['Alice', 'Bob', 'Charlie', 'Dan', 'Eve', 'Frank', 'Grace', 'Heidi', 'Ivan']
)
In [12]:
data.head(5)
Out[12]:
time event
Alice 1 True
Bob 4 True
Charlie 7 True
Dan 12 False
Eve 14 True

Survival function 추정

  • KaplanMeierFitter 사용
  • X축은 시간, Y축은 생존한 비율
  • 파란선이 의미하는 KM-estimate값이 시간이 지날수록 감소 → 시간이 지날수록 생존확률 낮다
  • 사각형의 영역은 신뢰구간을 의미 : 95% (alpha 기본값 = 0.05)
In [13]:
kmf = KaplanMeierFitter()
kmf.fit(data["time"], data["event"])
Out[13]:
<lifelines.KaplanMeierFitter:"KM_estimate", fitted with 9 total observations, 4 right-censored observations>
In [14]:
plot = kmf.plot_survival_function()
plot.set_xlabel('time (days)')
plot.set_ylabel('survival function, $\hat{S}(t)$')
plot
Out[14]:
<matplotlib.axes._subplots.AxesSubplot at 0x189b53eb710>

Cumulative hazard function

  • Nelson-Aalen etimator 사용
  • X축은 시간, Y축은 누적된 위험도 의미
  • 파란선이 의미하는 NA_estimate 값이 시간이 지날수록 감소 → 시간이 지날수록 증가(이탈 위험 증가 의미)
  • 사각형의 영역은 신뢰구간을 의미 : 95% (alpha 기본값 = 0.05)
In [21]:
naf = NelsonAalenFitter()
naf.fit(data["time"], data["event"])
Out[21]:
<lifelines.NelsonAalenFitter:"NA_estimate", fitted with 9 total observations, 4 right-censored observations>
In [22]:
plot = naf.plot_cumulative_hazard()
plot.set_xlabel('time (days)')
plot.set_ylabel('cumulative hazard function, $\hat{Λ}(t)$')
plot
Out[22]:
<matplotlib.axes._subplots.AxesSubplot at 0x189b76074a8>

이탈방지 평가

  • 그래프를 육안으로 살펴보면 프로모션 했을때(=experiment 값) 효과가 있는 것으로 보임
  • 생존함수에서 experiment를 보면 control보다 생존확률이 높은 것을 볼 수 있음
  • 위험함수에서 experiment를 보면 control보다 위험도가 낮은 것을 볼 수 있음
In [30]:
data_A = pd.DataFrame(
    {
        'time': [2, 4, 9, 12, 19, 23, 26, 41, 48],
        'event': [True, True, False, False, False, True, True, False, False]
    },
    index = ['Alice_A', 'Bob_A', 'Charlie_A', 'Dan_A', 'Eve_A', 'Frank_A', 'Grace_A', 'Heidi_A', 'Ivan_A']
)

data_B = pd.DataFrame(
    {
        'time': [1, 4, 7, 12, 14, 20, 26, 40, 45],
        'event': [True, True, True, False, True, False, True, False, False]
    },
    index = ['Alice_B', 'Bob_B', 'Charlie_B', 'Dan_B', 'Eve_B', 'Frank_B', 'Grace_B', 'Heidi_B', 'Ivan_B']
)
In [31]:
data_A
Out[31]:
time event
Alice_A 2 True
Bob_A 4 True
Charlie_A 9 False
Dan_A 12 False
Eve_A 19 False
Frank_A 23 True
Grace_A 26 True
Heidi_A 41 False
Ivan_A 48 False
In [32]:
data_B
Out[32]:
time event
Alice_B 1 True
Bob_B 4 True
Charlie_B 7 True
Dan_B 12 False
Eve_B 14 True
Frank_B 20 False
Grace_B 26 True
Heidi_B 40 False
Ivan_B 45 False
In [34]:
kmf = KaplanMeierFitter()
kmf.fit(data_A["time"], data_A["event"], label="experiment")
ax_kmf = kmf.plot()
kmf.fit(data_B["time"], data_B["event"], label="control")
ax_kmf = kmf.plot(ax=ax_kmf)

ax_kmf.set_xlabel('time (days)')
ax_kmf.set_ylabel('survival function, $\hat{S}(t)$')
ax_kmf
Out[34]:
<matplotlib.axes._subplots.AxesSubplot at 0x189b8f0d6d8>
In [35]:
naf = NelsonAalenFitter()
naf.fit(data_A["time"], data_A["event"], label="experiment")
ax_naf = naf.plot()
naf.fit(data_B["time"], data_B["event"], label="control")
ax_naf = naf.plot(ax=ax_naf)

ax_naf.set_xlabel('time (days)')
ax_naf.set_ylabel('cumulative hazard function, $\hat{Λ}(t)$')
ax_naf
Out[35]:
<matplotlib.axes._subplots.AxesSubplot at 0x189b965a4e0>

유의성 검증 : Logrank test

  • p-value가 0.6792로 1종오류를 범할 확률이 높게 나타남
  • 따라서, 유의하다고 판단할 수 없음
In [38]:
logrank_test(data_A["time"], data_B["time"], data_A["event"], data_B["event"]).p_value
Out[38]:
0.6791912509835747