--10.데이터분석으로 심부전증을 예방할 수 있을까?.ipynb--
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import os
base_path = r'/content/drive/MyDrive/dataset'
file_path = os.path.join(base_path, 'heart_failure_clinical_records_dataset.csv')
df = pd.read_csv(file_path)
df
df.head()
df.info() # 결측치가 있는지 없는지, 데이터 타입은 어떻게 이루어져있는 것인가.
"""
현재 EDA 를 수행하고 있습니다.
데이터들의 숫자드을 쓱 눈으로 보는것 먼저 해봅시다.
age : 환자의 나이 (숫자형) <- 정수값일줄 알았는데 float 타입으로 읽혀짐. 확인해볼 필요 있음.
anaemia : 빈혈증 여부 0, 1 값만 가지고 있슴 확인 (분류형) (0: 정상, 1: 빈혈)
creatinine_phosphokinase : 크레아틴키나제 검사 결과 (숫자형, 수치형)
diabetes : 당뇨병 여부 0, 1 값만 가지고 있슴 확인 (분류형) (0: 정상, 1: 당뇨병)
ejection_fraction : 박출계수 (%) 심박이 이루어질때 나오는 피의 비율 0 ~ 100 (숫자형, 수치형)
high_blood_pressure : 고혈압 여부. 0, 1 값만 가지고 있슴 확인 (분류형) (0: 정상, 1: 고혈압)
platelets : 혈소판 수치 (kiloplatelets/mL) 단위. 혈액 ml 당 (숫자형, 수치형)
serum_creatinine: 혈중 크레아틴 레벨 (mg/dL) (숫자형, 수치형)
serum_sodium: 혈중 나트륨 레벨 (mEq/L) (수치형)
sex: 성별 0, 1 값만 가지고 있슴 확인 (분류형) (0: 여성, 1: 남성)
smoking: 흡연 여부 (분류형) (0: 비흡연, 1: 흡연)
time: 관찰기간 (일) (숫자형, 수치형)
DEATH_EVENT: 사망 여부 (분류형) (0: 생존, 1: 사망) <- 타겟값.
각 컬럼들이
"""
None
"""
info() 중요하다
특히 Non-Null 개수와 Dtype !!
지금은 총 299개의 ROW가 있고 인덱스 0 ~ 298
Non-null 이 '전부' 299 : 결측치(missing value) 확인
총 3개의 float 와 10개의 int로 되어 있슴.
간혹 데이터 타입이 잘 정의 되어 있지 않은 경우 object (파이썬 객체)로 되어 있는 경우가 있다.
그런 경우에는 데이터 타입을 변환해주어야 하는 전처리를 해주어야 할때도 있습니다.
지금은 그렇게 하지 않아도 될만큼 깔끔하게 정리되어 있다.
매우 깔끔(클린)한 데이터 셋이다!
비록 dtype 은 int 타입이나 '분류형'으로 보아야 하는 데이터들이 있다.
ex) DEATH_EVENT, smoking, sex, anaemia, diabetes, high_blood_pressure
"""
None
df.describe()
이상치 (Outlier)란 관측된 데이터의 범위에서 많이 벗어나 아주 작은 값이나 아주 큰 값

![]()
이상치 탐색을 위해 박스플롯으로 시각화하곤 합니다. 박스플롯은 다음과 같은 원리로 그려집니다.
박스플롯은 분위수를 기준으로 그려집니다.
상자 안에 그려져 있는 직선은 중위수(Median)을 나타냅니다.
상자의 밑변은 1분위수를 나타내며, 윗변은 3분위수를 나타냅니다.
상자를 중심으로 위 아래에, 직선이 있는 것을 볼 수 있습니다.
이 직선은 울타리라고 부릅니다.
상자로부터 아래 직선의 계산식 : Q1 - 1.5 (Q3 - Q1)
상자로부터 위 직선의 계산식 : Q3 + 1.5 (Q3 - Q1)
이 울타리를 벗어난 값들을 Outlier라고 부릅니다.
Outlier는 기본적으로 통계추정에 있어서 방해가 되고는 합니다.
통계분석은 전부 귀납법인데, 이상치같은 특수 케이스가 규칙을 만드는데 방해가 되기 때문입니다.
제거를 하는 방법 (단점: 데이터가 버려진다)
데이터 변형을 통해 Outlier문제를 줄여줍니다.
df.describe()
"""
숫자로만 보면 알기 어려운 것들이 있다.
그래도 확인해볼수 있는 것들은
max, min 값이 과도하게 크거나 작은 것들
예를 들어 creatinine_phosphokinase 의 경우
min 값이 23 d이고 중앙값이 250 인데... max 값이 7861 이다!?!?
앞의 것들 min ~ 75% 의 증가세에 비하면 max 값이 상당히 큰 값이다. 상당한 outlier 에 해당한다 볼수 있다.
상위 몇개 데이터는 배제해볼 필요성도 있겠구나.. 라고 생각해볼수 잇습니다.
ejection_fraction : 크게 문제 없어 보인다
platelets(혈소판 수치) : 크게 문제 없어 보인다 . 증가세가 일정한 느낌
serum_sodium : 크게 문제 없어 보인다. 그러나 값의 범위가 좁은 느김
113 ~ 148 범위. 평균이 136 인데... std 가 4.4. 정도밖에 안된다.
time : 최소 4일 최대 285일 관찰 .. 285일이면 약 9개월 정도 관찰.
나중에 time 과 DEATH_EVENT 의 관계를 보면 될거 같다.
사망을 하게 되면 관찰이 멈출테니까.. 상당히 correlate 된 데이터라 볼수 있을듯.
잠시후에 다시 이야기 하도록 하죠.
"""
None
sns.histplot(x = 'age', data = df)
sns.histplot(x = 'age', data = df, hue='DEATH_EVENT')
"""
DEATH_EVENT=1 : 사망하신 분들이 나이대별로 고루 분포되어 있다.
DEATH_EVENT=0 : 사망하지 않은분들은 나이가 젊은 쪽에 몰려 있슴을 확인할수 있다
"""
None
sns.histplot(x = 'age', data = df, hue='DEATH_EVENT', kde=True)
"""
상당히 범위가 많이 겹쳐 있기 때문에
age 가 DEATH_EVENT 를 가르는데에는 크게 유용하진 않을거 같습니다.
"""
None
df.columns
df.describe()
sns.histplot(x='creatinine_phosphokinase', data=df)
"""
일전의 '기술통계량'에서 보았듯이 outlier가 많다!
지금의 histogram 으로 유용한 데이터 얻기가 쉽지 않을 수 있다.
그래서 outlier를 배제하고 들여다보자. 3000이상은 떼어내고 보자.
"""
None
sns.histplot(x='creatinine_phosphokinase', data=df.loc[df['creatinine_phosphokinase'] < 3000])
sns.histplot(x='ejection_fraction', data=df)
sns.histplot(x='ejection_fraction', data=df, bins=13, hue='DEATH_EVENT', kde=True)
sns.histplot(x='platelets', data=df, hue='DEATH_EVENT', kde=True)
jointplo
df.columns
sns.jointplot(x='platelets', y='creatinine_phosphokinase', hue='DEATH_EVENT', data=df, alpha=0.3)
"""
scatter 플롯으로 보이는 분포가 밑에 많이 뭉쳐있어서
DEATH_EVENT를 가르는데 도움이 되지는 않을 것 같다.
"""
None
sns.boxplot(x='smoking', y='ejection_fraction', data=df, hue='smoking')
sns.violinplot(x='DEATH_EVENT', y='ejection_fraction', data=df, hue='DEATH_EVENT')
sns.violinplot(x='DEATH_EVENT', y='ejection_fraction', data=df, hue='smoking')
sns.swarmplot(x='DEATH_EVENT', y='platelets', data=df, hue='DEATH_EVENT')
sns.swarmplot(x='DEATH_EVENT', y='platelets', data=df, hue='smoking',)
countplot() 사용
sns.countplot(x='DEATH_EVENT', data=df, hue='DEATH_EVENT')
df.describe()
sns.countplot(x='sex', data=df, hue='DEATH_EVENT')
df['age']
기존의 컬럼대신 새로운 컬럼데이터를 만든뒤 이를 분석에 활용
df['age_span'] = (df['age']/10).astype(int) * 10
df
df.groupby('age_span').size()
df.groupby('age_span').mean()
ab = df.groupby('age_span').sum()['DEATH_EVENT']
ab
plt.bar(ab.index, ab)
df2 = pd.DataFrame(ab)
df2
df2.sort_values(by='DEATH_EVENT')
df2.sort_values(by='DEATH_EVENT', ascending=False).plot(kind='bar')
df.columns
sns.pairplot(df[['age', 'creatinine_phosphokinase', 'ejection_fraction', 'DEATH_EVENT']], hue='DEATH_EVENT')
from sklearn.preprocessing import StandardScaler
df.columns
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']
X_num # 수치형 데이터
X_cat # 범주형 데이터
y
scaler = StandardScaler()
scaler.fit(X_num)
X_scaled = scaler.transform(X_num) # 결과는 numpy array 였다.
X_scaled = pd.DataFrame(data=X_scaled, index = X_num.index, columns=X_num.columns)
X_scaled
X_scaled.describe()
X = pd.concat([X_scaled, X_cat], axis=1)
X
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1)
X_train.shape, X_test.shape
y_train.shape, y_test.shape
from sklearn.linear_model import LogisticRegression
model_lr = LogisticRegression()
model_lr.fit(X_train, y_train)
model_lr.get_params()
model_lr = LogisticRegression(max_iter=1000)
model_lr.fit(X_train, y_train)
from sklearn.metrics import classification_report
pred = model_lr.predict(X_test)
print(classification_report(y_test, pred))
분류형에서 쓰임.
from xgboost import XGBClassifier
model_xgb = XGBClassifier()
model_xgb.fit(X_train, y_train)
pred = model_xgb.predict(X_test)
print(classification_report(y_test, pred))
머신러닝 학습 이후에 가능한것.
modelxgb.feature_importances # 이번 모델에서의 각 feature의 중요도
modelxgb.feature_importances.sum()
plt.bar(X.columns, modelxgb.feature_importances)
plt.xticks(rotation=90)
df.columns
sns.jointplot(x='ejection_fraction', y='serum_creatinine', data=df, hue='DEATH_EVENT', alpha=0.3)
from sklearn.metrics import PrecisionRecallDisplay, precision_recall_curve
fig = plt.figure()
ax = fig.gca() # get current axes
PrecisionRecallDisplay.from_estimator(model_lr, X_test, y_test, ax=ax)
PrecisionRecallDisplay.from_estimator(model_xgb, X_test, y_test, ax=ax)
from sklearn.metrics import RocCurveDisplay, roc_curve
model_lr.predict_proba(X_test)[:10]
pred_prob = model_lr.predict_proba(X_test)[:, 1]
fprs, tprs, thresholds = roc_curve(y_test, pred_prob)
plt.plot(fprs,tprs,label='ROC')
fig = plt.figure()
ax = fig.gca()
proba_lr = model_lr.predict_proba(X_test)[:, 1]
proba_xgb = model_xgb.predict_proba(X_test)[:, 1]
fprs, tprs, thresholds = roc_curve(y_test, proba_lr)
ax.plot(fprs, tprs, label='Logistic Regression')
fprs, tprs, thresholds = roc_curve(y_test, proba_xgb)
ax.plot(fprs, tprs, label='XGBBoost')
ax.set_xlabel('FPR: Flase Positive Rate')
ax.set_xlabel('TPR: True Positive Rate')
fig.legend()