Cox 비례위험모형 (Cox Proportional Hazards Model) 은 생존 시간에 영향을 미치는 다수의 공변량(설명 변수) 을 고려해, 각 변수의 위험도(Hazard)에 미치는 영향력(계수) 을 추정에 쓰인다.
위험률 비율(Hazard Ratio)은 시간이 지나도 변하지 않는다고 가정한다는 ("Proportional Hazards") 가 주요 목표이다.
kaplan-meier 와의 차이
| 구분 | Kaplan-Meier Curve | CoxPHFitter의 생존 함수 |
| 목적 | 기술(Descriptive) | 예측(Predictive) |
| 의미 | 데이터에 있는 집단 전체의 과거 생존 경험이 어떠했는지 보여줌 | 여러 변수를 고려해 특정 개인의 미래 생존 확률이 어떨지 예측함 |
| 사용 데이터 | 오직 기간(duration)과 사건(event) | 기간, 사건 + 개인별 특성 (X) |
| 결과물 | 집단 전체에 대한 하나의 생존 곡선 | 개인별로 각기 다른 생존 곡선 |
| 핵심 질문 | "이 그룹은 과거에 어떻게 생존했나?" | "이 사용자는 미래에 어떻게 생존할까?" |
Kaplan-Meier curve는 특정 그룹의 과거 데이터를 요약해서 보는 '사실에 대한 그림'
CoxPHFitter가 만들어주는 생존 함수는 여러 원인을 분석해 미래를 내다보는 '예측에 대한 그림'
실습
데이터 : https://www.kaggle.com/datasets/blastchar/telco-customer-churn
from lifelines import CoxPHFitter
import pandas as pd
# 데이터 전처리
df = pd.read_csv("Telco-Customer-Churn.csv")
df = df[df["TotalCharges"].apply(lambda x: str(x).strip() != "")]
df["TotalCharges"] = df["TotalCharges"].astype(float)
df["Churn_event"] = df["Churn"].map({"Yes": 1, "No": 0})
# 분석에 사용할 변수 선택
cox_df = df[["tenure", "MonthlyCharges", "TotalCharges", "Churn_event"]].copy()
cox_df = cox_df.dropna()
# 모델 학습
cph = CoxPHFitter()
cph.fit(cox_df, duration_col="tenure", event_col="Churn_event")
# 결과 출력
cph.print_summary()


- Concordance = 0.90
Concordance index는 0.5~1.0 사이 값을 가지며, 0.90은 매우 높은 예측 성능을 가진다. 즉, 고객의 이탈 시점을 예측하는 데 상당히 정확한 모델이 되었다. - log-likelihood ratio test = 4598.33 on 2 df (p-value < 0.005)
두 개의 독립변수(MonthlyCharges, TotalCharges)가 모델에 유의미하게 기여하고 있음을 보여준다.
-log2(p)가 inf인 것으로 보아 p-value는 거의 0에 수렴 → 통계적으로 매우 유의함
칼럼 데이터 의미
MonthlyCharges 0.07 exp(0.07) = 1.07
월 요금이 1 단위 증가할 때 이탈 위험이 약 7% 증가로 매우 중요한 칼럼이다.
TotalCharges -0.00 계수가 0에 가까워 해석이 어려움.
exp(coef)=1.00 -> 이탈에 거의 영향 없음으로 보입니다. (모델에서 제거해도 큰 차이 없을 수 있다는 뜻)
다음은 cph로 학습된 모델로 생존 확률을 구하는 코드이다.
특정 몇명은 20일째에 60% 확률로 살아남으므로, 이탈할 확률이 점차 높아지는것을 알 수 있다.
df = pd.read_csv("Telco-Customer-Churn.csv")
df = df[df["TotalCharges"].apply(lambda x: str(x).strip() != "")]
df["TotalCharges"] = df["TotalCharges"].astype(float)
df["Churn_event"] = df["Churn"].map({"Yes": 1, "No": 0})
# 분석에 사용할 변수 선택
cox_df = df[["tenure", "MonthlyCharges", "TotalCharges", "Churn_event"]].copy()
new_data = cox_df[cox_df['Churn_event'] == 0].head(10) # 탈퇴하지 않은 10명 데이터
survival_function = cph.predict_survival_function(new_data)
survival_probs = survival_function.loc[0:7].iloc[:, 0].to_dict()
plt.figure(figsize=(12, 7))
survival_function.plot(ax=plt.gca())
plt.title('User Survival Probability Over Time')
plt.xlabel('Duration (Days)')
plt.ylabel('Survival Probability')
plt.grid(True)
plt.legend(title='User ID')
plt.show()

반대로 탈퇴했던 사람들의 데이터로 보면 시작과 동시에 엄청나게 떨어지는 몇몇 유저를 볼 수 있다.
이런걸 토대로 미래에 어떤 유저가 탈퇴할지를 예측할수 있다.

'ML > 데이터 분석' 카테고리의 다른 글
| [airflow] certified (0) | 2025.10.29 |
|---|---|
| [airflow] Branching (0) | 2025.10.27 |
| 2. 3점 슛이 코트를 지배하는가? (1) | 2025.07.05 |
| 1. 영화관은 정말 망해가고 있는걸까? (0) | 2025.06.29 |
| Error: unsupported locale setting (1) | 2024.07.31 |