라일락 꽃이 피는 날

Heart Failure Prediction 본문

데이터 분석/실습

Heart Failure Prediction

eunki 2021. 12. 22. 18:27
728x90

https://www.kaggle.com/andrewmvd/heart-failure-clinical-data

 

Heart Failure Prediction

12 clinical features por predicting death events.

www.kaggle.com

 

 

 

데이터 정보

  • age: 환자의 나이
  • anaemia: 환자의 빈혈증 여부 (0: 정상, 1: 빈혈)
  • creatinine_phosphokinase: 크레아틴키나제 검사 결과
  • diabetes: 당뇨병 여부 (0: 정상, 1: 당뇨)
  • ejection_fraction: 박출계수 (%)
  • high_blood_pressure: 고혈압 여부 (0: 정상, 1: 고혈압)
  • platelets: 혈소판 수 (kiloplatelets/mL)
  • serum_creatinine: 혈중 크레아틴 레벨 (mg/dL)
  • serum_sodium: 혈중 나트륨 레벨 (mEq/L)
  • sex: 성별 (0: 여성, 1: 남성)
  • smoking: 흡연 여부 (0: 비흡연, 1: 흡연)
  • time: 관찰 기간 (일)
  • DEATH_EVENT: 사망 여부 (0: 생존, 1: 사망)

 

 

 

데이터셋 준비

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# pd.read_csv()로 csv파일 읽어들이기
df = pd.read_csv('../input/heart-failure-clinical-data/heart_failure_clinical_records_dataset.csv')

 

 

 

 

EDA 및 데이터 기초 통계 분석

df.head()

수치형 데이터: age, creatinine_phosphokinase, ejection_fraction, platelets, serum_creatinine, serum_sodium, time
범주형 데이터: anaemia, diabetes, high_blood_pressure, sex, smoking
타겟 데이터: DEATH_EVENT

 

 

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 299 entries, 0 to 298
Data columns (total 13 columns):
 #   Column                    Non-Null Count  Dtype  
---  ------                    --------------  -----  
 0   age                       299 non-null    float64
 1   anaemia                   299 non-null    int64  
 2   creatinine_phosphokinase  299 non-null    int64  
 3   diabetes                  299 non-null    int64  
 4   ejection_fraction         299 non-null    int64  
 5   high_blood_pressure       299 non-null    int64  
 6   platelets                 299 non-null    float64
 7   serum_creatinine          299 non-null    float64
 8   serum_sodium              299 non-null    int64  
 9   sex                       299 non-null    int64  
 10  smoking                   299 non-null    int64  
 11  time                      299 non-null    int64  
 12  DEATH_EVENT               299 non-null    int64  
dtypes: float64(3), int64(10)
memory usage: 30.5 KB

(299, 13) → 299 rows 13 columns
null 데이터 없음

 

 

df.describe()

creatinine_phosphokinase의 최댓값이 비정상적으로 큰 것으로 보인다 → outlier

 

 

# seaborn의 histplot()로 히스토그램 그리기
# hue 옵션으로 데이터 비교, 겹치는 부분은 회색
# kde 옵션으로 커널밀도추정 그리기, 부드러운 분포 곡선
# bins 옵션으로 bin 개수 지정
sns.histplot(x='age', data=df, hue='DEATH_EVENT', kde=True)

70대 중후반 부터는 생존자보다 사망자의 수가 더 많고, 생존자의 수가 급격히 줄어든다
→ 나이는 생존율에 영향을 준다.

 
 
# sns.histplot(data=df['creatinine_phosphokinase'])
sns.histplot(data=df.loc[df['creatinine_phosphokinase'] < 3000, 'creatinine_phosphokinase'])

크레아틴키나제 수치(creatinine_phosphokinase)가 3000이 넘는 경우는 거의 없기 때문에 outlier로 분류한다.
creatinine_phosphokinase 범위를 3000 미만으로 정하고 히스토그램을 그린다.

 

 
sns.histplot(x='ejection_fraction', data=df, bins=13, hue='DEATH_EVENT', kde=True)

박출계수(ejection_fraction)가 낮을수록 사망할 확률이 높고, 25% 미만일 경우 생존자보다 사망자가 더 많다.
→ 박출계수는 생존율에 영향을 준다.

 

 
sns.histplot(x='platelets', data=df, bins=13, hue='DEATH_EVENT')

혈소판 수(platelets)는 생존율에 영향을 주는지 확실히 알 수 없다.

 

 

sns.histplot(x='time', data=df, hue='DEATH_EVENT', kde=True)

관찰 기간(time)이 짧을수록 생존자보다 사망자 수가 더 많은 것은 사실이지만, 사망했기 때문에 관찰 기간이 짧게 나올 수 있다.
time 데이터에는 이미 DEATH_EVENT 데이터의 결과가 포함된다는 것이다.
따라서, time 데이터는 배제하고 학습하는 것이 좋다.

 

 
# seaborn의 jointplot()로 산점도와 히스토그램을 함께 그리기
# alpha 옵션으로 투명도 조절, 겹치는 부분을 명확히 볼 수 있음
sns.jointplot(x='ejection_fraction', y='serum_creatinine', hue='DEATH_EVENT', data=df)

박출계수(ejection_fraction)가 높고 혈중 크레아틴 레벨(serum_creatinine)이 낮을수록 생존율이 높다.

 

 
# seaborn의 boxplot()로 박스플롯 그리기
sns.boxplot(x='DEATH_EVENT', y='ejection_fraction', data=df)
 
 
# seaborn의 violinplot()로 바이올린플롯 그리기
sns.violinplot(x='DEATH_EVENT', y='ejection_fraction', data=df)
 
 
# swarmplot = violinplot + scatterplot
# swarmplot에서는 hue 옵션 사용이 유용함
sns.swarmplot(x='DEATH_EVENT', y='platelets', hue='smoking', data=df)

 

 

 

데이터 전처리

from sklearn.preprocessing import StandardScaler
# 수치형 입력 데이터, 범주형 입력 데이터, 출력 데이터로 구분
X_num =  df[['age', 'creatinine_phosphokinase', 'ejection_fraction', 'platelets', 'serum_creatinine', 'serum_sodium']]
X_cat = df[['anaemia', 'diabetes', 'high_blood_pressure', 'sex', 'smoking']]
y = df['DEATH_EVENT']
# StandardScaler을 이용하여 수치형 데이터 표준화 진행
# 평균 0, 표준편차 1이 되도록 변환
scaler = StandardScaler()
scaler.fit(X_num)
X_scaled = scaler.transform(X_num)

# StandardScaler로 데이터를 전처리하면 numpy 형태로 바뀌기 때문에 다시 DataFrame 형태로 바꾸는 것이 좋음
X_scaled = pd.DataFrame(data=X_scaled, index=X_num.index, columns=X_num.columns)

# concat을 이용하여 수치형 데이터와 범주형 데이터를 병합
# axis=1 옵션으로 column끼리 합침
X = pd.concat([X_scaled, X_cat], axis=1)
X.head()

 

 

 

학습 데이터와 테스트 데이터 분리

from sklearn.model_selection import train_test_split
# train_test_split을 이용하여 학습 데이터와 테스트 데이터 분리
# test_size=0.3 → 70% train, 30% test 데이터로 사용
# shuffle=False : 시간적 관계가 있어서 앞의 데이터로 학습하고 뒤의 데이터로 테스트해야 할 때 사용
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1)
X_train

 

 

 

Classification 모델 학습

1. Logistic Regression 모델

from sklearn.linear_model import LogisticRegression
# LogisticRegression 모델 생성/학습
# verbose=1 옵션으로 학습 과정 표현
# iteration이 부족하여 학습이 되지 않을 때는 max_iter 값 높이기
model_lr = LogisticRegression()
model_lr.fit(X_train, y_train)
 
 
 
from sklearn.metrics import classification_report
# classification_report을 이용하여 모델 학습 결과 평가
pred = model_lr.predict(X_test)
print(classification_report(y_test, pred))
              precision    recall  f1-score   support

           0       0.78      0.92      0.84        64
           1       0.64      0.35      0.45        26

    accuracy                           0.76        90
   macro avg       0.71      0.63      0.65        90
weighted avg       0.74      0.76      0.73        90

정확도(accuracy) = 0.76 (76%)

 

 

 

2. XGBoost 모델

from xgboost import XGBClassifier
# XGBClassifier 모델 생성/학습
model_xgb = XGBClassifier(eval_metric='mlogloss')
model_xgb.fit(X_train, y_train)
 
 
 
pred = model_xgb.predict(X_test)
print(classification_report(y_test, pred))
              precision    recall  f1-score   support

           0       0.81      0.88      0.84        64
           1       0.62      0.50      0.55        26

    accuracy                           0.77        90
   macro avg       0.72      0.69      0.70        90
weighted avg       0.76      0.77      0.76        90

정확도(accuracy) = 0.77 (77%)

 

 

# XGBClassifier 모델의 feature_importances_를 이용하여 피쳐 중요도 확인
plt.bar(X.columns, model_xgb.feature_importances_)
plt.xticks(rotation=90)
plt.show()

serum_creatinine와 ejection_fraction 피쳐 중요도가 높다.

 

 

 

모델 학습 결과 심화 분석

1. Precision-Recall 커브

from sklearn.metrics import plot_precision_recall_curve
# 두 모델의 Precision-Recall 커브 한 번에 그리기
# plt.figure() : 그래프가 그려지는 전체 캔버스
fig = plt.figure()
ax = fig.gca()
plot_precision_recall_curve(model_lr, X_test, y_test, ax=ax)
plot_precision_recall_curve(model_xgb, X_test, y_test, ax=ax)

 

 

 

2. ROC 커브

from sklearn.metrics import plot_roc_curve
# 두 모델의 ROC 커브 한 번에 그리기
fig = plt.figure()
ax = fig.gca()
plot_roc_curve(model_lr, X_test, y_test, ax=ax)
plot_roc_curve(model_xgb, X_test, y_test, ax=ax)

 

AUC = ROC 커브 아래 영역
AUC가 클수록 좋으므로 LogisticRegression 모델의 성능이 XGBoost 모델보다 조금 더 좋다.

728x90

'데이터 분석 > 실습' 카테고리의 다른 글

Used Cars  (0) 2022.01.03
US Election 2020  (0) 2021.12.27
European Soccer  (0) 2021.12.27
League of Legends Diamond Ranked Games (10 min)  (0) 2021.12.23
Students' Academic Performance  (0) 2021.12.23