본문 바로가기

ML/데이터 분석

CoxPHFitter

 

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