생존분석
생존분석(Survival Analysis)은 사건 발생까지의 시간을 분석하는 통계적 기법으로, 주로 의학 연구에서 환자의 생존 기간을 분석하거나, 신제품의 고장 시간 등을 예측하는 데 사용됩니다. 생존분석은 단순히 사건 발생 여부뿐만 아니라, 사건이 발생하기까지의 시간 정보를 활용하여 보다 정밀한 분석을 가능하게 합니다. 이 장에서는 **카플란-마이어 추정법(Kaplan-Meier Estimator)**과 **콕스 비례위험 모델(Cox Proportional Hazards Model)**에 대해 자세히 살펴보고, 각 방법에 대한 파이썬 코드 예시를 제공하겠습니다.
10. 생존분석 (Survival Analysis)
개요
생존분석은 주어진 시간 동안 특정 사건(예: 사망, 고장, 재발 등)이 발생하지 않을 확률을 분석하는 기법입니다. 생존분석은 다음과 같은 특징을 가집니다:
- 검열(Censoring): 연구 기간 동안 사건이 발생하지 않아 정확한 사건 발생 시간이 알 수 없는 데이터를 처리합니다.
- 생존함수(Survival Function): 시간 까지 사건이 발생하지 않을 확률을 나타냅니다. tt
- 위험함수(Hazard Function): 시간 에서 사건이 발생할 즉각적인 위험률을 나타냅니다. tt 생존분석은 주로 의학 연구에서 환자의 생존 기간을 분석하거나, 공학 분야에서 기계의 고장 시간을 예측하는 데 사용됩니다.
주요 방법
- 카플란-마이어 추정법 (Kaplan-Meier Estimator): 생존 곡선을 추정하는 비모수적 방법입니다.
- 콕스 비례위험 모델 (Cox Proportional Hazards Model): 생존 시간에 영향을 미치는 요인을 분석하는 반모수적 모델입니다.
10.1 카플란-마이어 추정법 (Kaplan-Meier Estimator)
설명
카플란-마이어 추정법은 생존 데이터를 분석하여 시간에 따른 생존 확률을 추정하는 비모수적 방법입니다. 이 방법은 검열된 데이터를 효과적으로 처리할 수 있으며, 여러 집단 간의 생존 곡선을 비교하는 데 유용합니다.
- *생존함수 *는 시간 까지 사건이 발생하지 않을 확률을 나타내며, 카플란-마이어 추정법을 통해 이를 추정합니다. S(t)S(t) tt
파이썬 코드 예시
아래 예제에서는 lifelines 라이브러리를 사용하여 카플란-마이어 추정법을 적용하는 방법을 보여줍니다. 예제 데이터는 환자의 생존 시간과 사건 발생 여부(1: 사건 발생, 0: 검열)를 포함합니다.
# 필요한 라이브러리 설치 (이미 설치되어 있다면 생략 가능)
# !pip install lifelines seaborn matplotlib
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from lifelines import KaplanMeierFitter
# 예제 데이터 생성
# 생존 시간 (개월)
np.random.seed(42)
data = pd.DataFrame({
'Survival_Time': np.random.exponential(scale=10, size=100),
'Event': np.random.binomial(1, 0.7, size=100) # 사건 발생 여부 (1: 발생, 0: 검열)
})
# 데이터 시각화: 생존 시간 분포
sns.histplot(data['Survival_Time'], bins=30, kde=True)
plt.title('생존 시간 분포')
plt.xlabel('생존 시간 (개월)')
plt.ylabel('빈도수')
plt.show()
# 카플란-마이어 추정법 적용
kmf = KaplanMeierFitter()
# 전체 생존 곡선
plt.figure(figsize=(10, 6))
kmf.fit(durations=data['Survival_Time'], event_observed=data['Event'], label='전체')
kmf.plot_survival_function(ci_show=True)
plt.title('카플란-마이어 생존 곡선')
plt.xlabel('시간 (개월)')
plt.ylabel('생존 확률')
plt.show()
# 그룹별 생존 곡선 비교 예시
# 예제 데이터에 그룹 추가 (예: 치료군과 대조군)
data['Group'] = np.random.choice(['치료군', '대조군'], size=100)
plt.figure(figsize=(10, 6))
for group in data['Group'].unique():
mask = data['Group'] == group
kmf.fit(durations=data.loc[mask, 'Survival_Time'], event_observed=data.loc[mask, 'Event'], label=group)
kmf.plot_survival_function(ci_show=True)
plt.title('그룹별 카플란-마이어 생존 곡선')
plt.xlabel('시간 (개월)')
plt.ylabel('생존 확률')
plt.legend()
plt.show()
# 생존 곡선 비교 통계적 검정 (로그-랭크 검정)
from lifelines.statistics import logrank_test
group1 = data[data['Group'] == '치료군']
group2 = data[data['Group'] == '대조군']
results = logrank_test(group1['Survival_Time'], group2['Survival_Time'],
event_observed_A=group1['Event'],
event_observed_B=group2['Event'])
results.print_summary()
설명
- 데이터 생성 및 시각화:
- 생존 시간은 지수분포를 따르도록 생성하였으며, 사건 발생 여부는 이항 분포를 사용하여 설정했습니다.
- 히스토그램을 통해 생존 시간의 분포를 시각화하였습니다.
- 카플란-마이어 추정법 적용:
KaplanMeierFitter객체를 생성하고,fit메소드를 사용하여 생존 데이터를 적합시킵니다.plot_survival_function메소드를 사용하여 생존 곡선을 시각화합니다.- 전체 데이터뿐만 아니라, 그룹별로 생존 곡선을 비교할 수 있습니다.
- 로그-랭크 검정(Log-Rank Test):
- 두 그룹 간의 생존 곡선 차이가 통계적으로 유의미한지를 검정하기 위해 로그-랭크 검정을 수행합니다.
logrank_test함수를 사용하여 검정을 수행하고, 결과를 출력합니다.
출력 예시
- 생존 곡선 그래프: 시간에 따른 생존 확률을 나타내는 곡선이 그려집니다. 그룹별로 비교할 경우, 각 그룹의 생존 곡선이 함께 표시됩니다.
- 로그-랭크 검정 결과:
# 로그-랭크 검정 결과 출력 예시
--------------------------------------------------------------------------------
test_statistic 1.2345
p-value 0.2678
alternative two-sided
method Log-rank test
support grouped
--------------------------------------------------------------------------------
- 해석: p-값이 0.05보다 크면 귀무가설(두 그룹의 생존 곡선이 동일함)을 기각하지 못하고, 두 그룹 간의 생존 곡선 차이가 유의미하지 않다고 결론지을 수 있습니다.
시각화 (옵션)
생존 곡선을 보다 직관적으로 이해하기 위해 여러 가지 시각화 방법을 활용할 수 있습니다. 예를 들어, 히스토그램과 KDE를 통해 각 그룹의 생존 시간 분포를 비교하거나, 생존 곡선에 신뢰구간을 추가하여 불확실성을 시각화할 수 있습니다.
# 생존 곡선에 신뢰구간 추가
plt.figure(figsize=(10, 6))
kmf.fit(durations=data['Survival_Time'], event_observed=data['Event'], label='전체')
ax = kmf.plot_survival_function(ci_show=True)
plt.title('카플란-마이어 생존 곡선 (신뢰구간 포함)')
plt.xlabel('시간 (개월)')
plt.ylabel('생존 확률')
plt.show()
10.2 콕스 비례위험 모델 (Cox Proportional Hazards Model)
설명
콕스 비례위험 모델(Cox Proportional Hazards Model)은 생존 시간에 영향을 미치는 여러 요인을 동시에 분석할 수 있는 반모수적 모델입니다. 이 모델은 위험함수(Hazard Function)를 설명 변수들의 선형 조합으로 표현하며, 주요 가정은 모든 설명 변수에 대해 위험 비율(Hazard Ratio)이 일정하다는 비례위험 가정(Proportional Hazards Assumption) 입니다.
- *위험함수 *는 시간 에서 사건이 발생할 즉각적인 위험률을 나타내며, 콕스 모델은 다음과 같이 표현됩니다: h(t)h(t) tt h(t∣X)=h0(t)exp(β1X1+β2X2+⋯+βpXp)h(t | X) = h_0(t) \exp(\beta_1 X_1 + \beta_2 X_2 + \dots + \beta_p X_p)
- h0(t)h_0(t): 기준 위험함수 (Baseline Hazard)
- X1,X2,…,XpX_1, X_2, \dots, X_p: 설명 변수
- β1,β2,…,βp\beta_1, \beta_2, \dots, \beta_p: 회귀 계수
파이썬 코드 예시
아래 예제에서는 lifelines 라이브러리를 사용하여 콕스 비례위험 모델을 적용하는 방법을 보여줍니다. 예제 데이터는 환자의 생존 시간, 사건 발생 여부, 그리고 몇 가지 설명 변수를 포함합니다.
# 필요한 라이브러리 설치 (이미 설치되어 있다면 생략 가능)
# !pip install lifelines seaborn matplotlib
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from lifelines import CoxPHFitter
# 예제 데이터 생성
np.random.seed(42)
n = 200
data = pd.DataFrame({
'Age': np.random.normal(60, 10, n), # 나이
'Sex': np.random.binomial(1, 0.5, n), # 성별 (0: 여성, 1: 남성)
'Treatment': np.random.binomial(1, 0.6, n), # 치료 여부 (0: 대조군, 1: 치료군)
'Survival_Time': np.random.exponential(scale=10, size=n), # 생존 시간
'Event': np.random.binomial(1, 0.7, n) # 사건 발생 여부 (1: 발생, 0: 검열)
})
# 데이터 전처리: 범주형 변수 인코딩 (필요 시)
data['Sex'] = data['Sex'].astype('category')
data['Treatment'] = data['Treatment'].astype('category')
# 데이터 시각화: 생존 시간과 치료 여부 간의 관계
sns.boxplot(x='Treatment', y='Survival_Time', data=data)
plt.title('치료군과 대조군의 생존 시간 비교')
plt.xlabel('치료 여부')
plt.ylabel('생존 시간 (개월)')
plt.show()
# 콕스 비례위험 모델 적합
cph = CoxPHFitter()
cph.fit(data, duration_col='Survival_Time', event_col='Event')
# 모델 요약 출력
cph.print_summary()
# 위험 비율(Hazard Ratios) 시각화
cph.plot()
plt.title('위험 비율 (Hazard Ratios)')
plt.show()
# 생존 함수 예측 및 시각화
plt.figure(figsize=(10, 6))
cph.plot_partial_effects_on_outcome(covariates='Age', values=[50, 60, 70], cmap='coolwarm')
plt.title('나이에 따른 생존 함수 변화')
plt.xlabel('나이')
plt.ylabel('생존 확률')
plt.show()
설명
- 데이터 생성 및 시각화:
Age,Sex,Treatment등의 설명 변수를 포함한 생존 데이터를 생성합니다.- 박스플롯을 통해 치료군과 대조군 간의 생존 시간 분포를 시각화합니다.
- 콕스 비례위험 모델 적합:
CoxPHFitter객체를 생성하고,fit메소드를 사용하여 모델을 적합시킵니다.print_summary메소드를 통해 모델의 회귀 계수, 위험 비율(Hazard Ratios), p-값 등을 확인합니다.
- 위험 비율 시각화:
plot메소드를 사용하여 각 설명 변수의 위험 비율을 시각화합니다. 위험 비율이 1보다 크면 해당 변수의 값이 증가할수록 사건 발생 위험이 증가함을 의미하고, 1보다 작으면 감소함을 의미합니다.
- 생존 함수 예측 및 시각화:
- 특정 설명 변수(
Age)의 값에 따라 생존 함수를 예측하고, 이를 시각화하여 나이에 따른 생존 확률의 변화를 확인합니다.
출력 예시
- 모델 요약:
coef exp(coef) se(coef) z p
Age -0.02 0.98 0.01 -1.98 0.047
Sex 0.30 1.35 0.20 1.50 0.133
Treatment -0.50 0.61 0.25 -2.00 0.046
- 해석:
- Age: 나이가 증가할수록 사건 발생 위험이 감소하는 경향을 보이며, p-값이 0.05보다 작아 통계적으로 유의미합니다.
- Sex: 남성의 사건 발생 위험이 여성에 비해 약간 높으나, p-값이 0.05보다 커서 통계적으로 유의미하지 않습니다.
- Treatment: 치료군의 사건 발생 위험이 대조군에 비해 유의하게 감소합니다.
- 위험 비율 그래프: 각 변수의 위험 비율을 시각적으로 나타낸 그래프가 출력됩니다.
- 생존 함수 그래프: 나이에 따른 생존 확률의 변화를 시각화한 그래프가 출력됩니다.
비례위험 가정 검정
콕스 비례위험 모델의 핵심 가정인 비례위험 가정을 확인하기 위해 lifelines 라이브러리의 check_assumptions 메소드를 사용할 수 있습니다.
# 비례위험 가정 검정
cph.check_assumptions(data, show_plots=True)
설명
- 비례위험 가정 검정:
- 잔차 분석: Schoenfeld 잔차를 통해 비례위험 가정이 충족되는지 확인합니다.
- 시각적 검토: 각 변수에 대한 로그-로그 생존 곡선을 시각화하여 선형성을 확인합니다.
- 결과 해석: 비례위험 가정이 충족되지 않는 경우, 모델을 수정하거나 다른 모델을 고려해야 합니다.
출력 예시
/home/user/.local/lib/python3.8/site-packages/lifelines/fitters/coxph_fitter.py:...
Schoenfeld residuals plot for Age
Schoenfeld residuals plot for Sex
Schoenfeld residuals plot for Treatment
...
# 콘솔에 출력되는 경고나 메시지
- 해석:
- 만약 비례위험 가정이 충족되지 않는다고 판단되면, 해당 변수에 대해 시간 의존적인 효과를 모델에 추가하거나, 다른 모델을 고려해야 합니다.
주의사항
- 비례위험 가정: 콕스 모델은 비례위험 가정을 전제로 하므로, 이 가정이 충족되지 않으면 모델의 결과가 신뢰할 수 없게 됩니다.
- 검열 데이터 처리: 생존분석은 검열된 데이터를 효과적으로 처리할 수 있지만, 검열의 유형과 원인을 고려해야 합니다.
- 상호작용 효과: 여러 설명 변수 간의 상호작용 효과를 고려하여 모델을 구축할 수 있습니다.
요약
생존분석은 사건 발생까지의 시간을 분석하여 생존 확률을 추정하고, 생존 시간에 영향을 미치는 요인을 분석하는 데 유용한 통계적 기법입니다. 카플란-마이어 추정법은 생존 곡선을 비모수적으로 추정하여 시간에 따른 생존 확률을 시각화하고, 콕스 비례위험 모델은 여러 요인의 영향을 동시에 고려하여 생존 시간에 미치는 영향을 분석할 수 있습니다.
파이썬의 lifelines 라이브러리를 활용하면 생존분석을 손쉽게 수행할 수 있으며, matplotlib과 seaborn을 통해 결과를 시각적으로 이해할 수 있습니다. 생존분석을 적용할 때는 데이터의 특성과 모델의 가정을 신중하게 고려하여 적절한 방법을 선택하고, 결과를 해석하는 것이 중요합니다.
추가적인 질문이나 더 깊은 설명이 필요하시면 언제든지 문의해 주세요!