본문 바로가기

Machine, Deep Learning/Machine, Deep Learning 실습

Kaggle - Heart Disease Dataset (1)

반응형
SMALL

이번엔 Heart Disease의 데이터를 시각화, 분석을 해보도록 하겠습니다. 된다면 Machine Learning 모델링도 해보도록 하겠습니다. 데이터는 Kaggle Datasets에서의 Heart Disease에서 참고했습니다. 모델링까지 하게된다면 Kaggle의 Kernels를 참고하겠습니다. 데이터의 License는 Reddit API Terms에 있습니다.

 

이 데이터 셋은 원래라면 76개의 속성을 가지고 있어야하지만, 공공연 적인 사용을 위한 14개로 축소하여 배포되었습니다. "목표" 필드는 환자의 심장 질환의 존재를 나타내고 0에서 4까지의 정수로 표현되어 있습니다. 0이 환자 중에 심장 질환을 가지고 있지 않음을 나타냅니다.

 

우선 필요한 라이브러리를 불러오도록 하겠습니다.

import pandas as pd # Python에서 테이블화 된 데이터를 다루는데 가장 최적화 된 라이브러리입니다.
import numpy as np # numpy는 행렬이나 대규모 다차원 배열을 쉽게 처리할 수 있도록 하는 라이브러리입니다.
import matplotlib.pyplot as plt
import seaborn as sns # matplotlib을 기반으로 한 Visualization libarary 입니다.

import missingno as msno # NaN 값이 있는지 시각적으로 표현해주는 라이브러리입니다.

sns.set(style='white', context='notebook', palette='deep', font_scale=2.5)

 

필요한 라이브러리를 다 불러왔으니, 이젠 데이터를 불러와보도록 하겠습니다.

data = pd.read_csv('input/heart.csv')
data.columns

heart.csv에 담긴 데이터들의 칼럼은 위와 같이 14개가 있습니다. 하나하나 설명을 해보도록 하겠습니다.

1. age 나이 (int)
2. sex 성별 (1, 0 / int)
3. chest pain type (4 values) 가슴 통증 타입 (0 ~ 3 / int)
4. resting blood pressure 혈압
5. serum cholestoral in mg/dl 혈청 콜레스테롤
6. fasting blood sugar > 120 mg/dl 공복 혈당
7. resting electrocardiographic results 심전도
8. maximum heart rate achieved 최대 심장박동 수
9. exercise induced angina 운동 유도 협심증 (이게 뭐죠?)
10. oldpeak = ST depression induced by exercise relative to rest 노약 =운동에 의해 유발되는 St 우울증 (이건 또 뭐죠?)
11. the slope of the peak exercise ST segment ST 세그먼트의 기울기
12. number of major vessels (0-3) colored by flourosopy 혈관의수
13. thal : 3 = normal; 6 = fixed defect; 7 = reversable defect 뭔지 모르겠네요
data.count()

보니까 NaN 데이터가 없네요 ! 데이터를 바로 사용해도 될거 같습니다. target 데이터가 labels니까 저 column은 따로 떼어서 보도록 하겠습니다.

labels = data["target"]
data = data.drop(labels = ["target"], axis = 1)
data.columns

데이터를 분석할 준비를 마쳤습니다. 이제 데이터를 시각화 해보도록 할까요? 생각해보니, 굳이 나눌 필요가 없을거 같네요 ! 다시 합치도록 할게요 >_<

data = pd.read_csv('input/heart.csv')
del labels

우선은 심장 질환을 가지고 있는 사람과 없는 사람의 수를 구해보도록 하고 퍼센트로 나타내보도록 하겠습니다.

data["target"].value_counts()

f, ax = plt.subplots(1, 2, figsize=(18, 8))

data["target"].value_counts().plot.pie(ax=ax[0], shadow=True, explode=[0, 0.1], autopct='%1.1f%%')
ax[0].set_title('Pie plot - Heart Disease')
ax[0].set_ylabel('')
sns.countplot('target', data=data, ax=ax[1])
ax[1].set_title('Count plot - Heart Disease')

plt.show()

보시면 심장 질환을 가진 사람이 더 많네요? 그렇다면 본격적인 데이터 분석을 통해서 원인을 찾아보도록 합시다.

우선 나이부터 보도록 하겠습니다. 나이가 많아질수록 심장질환이 많은지를 보겠습니다.

f, ax = plt.subplots(1, 1, figsize=(8, 8))

data[['age', 'target']].groupby(['age'], as_index=True).count(ax=ax)

보니까 50대 후반의 많은 사람들이 심장 질환을 많이 가지고 있는거 같습니다.

그렇다면 나이별로 심장 질환의 비율을 알아볼까요?

f, ax = plt.subplots(1, 1, figsize=(18, 8))
sns.countplot('age', hue='target', data=data, ax=ax)

신기하게도 40대 초반 50대 초반이 심장 질환을 많이 가지고 있습니다. 좀 더 넓게 말하자면 40대에서 60대까지가 심장질환을 많이 가지고 있다고 말할 수 있겠습니다. 그리고 더 말을 붙히자면 50대 쪽에서 심장질환을 안가진 사람들의 절대적인 수가 더 많은 것을 볼 수 있습니다.

그럼 마지막으로 심장질환이 걸릴 때의 평균 나이를 구해보도록 하겠습니다.

data[['age', 'target']].groupby('target', as_index=True).mean()

나이와 thalach 데이터도 한번 확인해보도록 합시다. thalach는 maximum heart rate achieved 데이터라고 합니다. 제 생각엔 최고혈압 인거 같습니다.

plt.scatter(x=data.age[data.target==1], y=data.thalach[(data.target==1)], c="red")
plt.scatter(x=data.age[data.target==0], y=data.thalach[(data.target==0)])
plt.legend(["Disease", "Not Disease"])
plt.xlabel("Age")
plt.ylabel("Maximum Heart Rate")
plt.show()

사실, 지금까지 보인 나이 데이터는 절대적인 수를 보여줄 뿐이지 비율로 보이진 않아서 어느 나이대가 심장질환이 더 많다고 객관적으로 보긴 힘듭니다. 또한, 나이는 continuous형을 띄기 때문에 평균으로 나누기에 무리가 있을 수 있습니다. (그래도 그래프 그려보는 연습으로 한번 해봤습니다) 그렇다 일단 나이는 뒤로 미루고 조금 더 보기 쉬운 성별을 기준으로 구분지어보도록 하겠습니다.

data[['sex','target']].groupby(['sex'], as_index=True).mean().sort_values(by='target', ascending=False)

조금 충격적이네요 ? 남자보다 여자가 심장질환을 가질 확률이 더 높은 것을 볼 수 있습니다. 그렇다면 그 count를 세보도록 할까요?? 저희는 pandas의 crosstab 메소드를 사용해보도록 하겠습니다.

pd.crosstab(data['sex'],data['target'], margins=True)

count를 해서 심장질환의 비율을 확인했는데 확실히 여성분들이 심장 질환이 걸릴 위험이 많이 있는 걸 볼 수 있습니다. 저도 제 여자친구에게 좀 잘해야겠네요. 물론 없지만

이제 가슴 통증에 의한 심장 질환 수를 보도록 하겠습니다. 통증은 0에서 3까지 있고 각 통증마다 심장질환이 있는지 없는지를 보면 될 거 같습니다.

f, ax = plt.subplots(1, 1, figsize=(8,8))

sns.countplot('cp', hue='target', data=data)

이 데이터로 확신해졌습니다. 혹시나 가슴통증이 느껴진다면, 바로 병원으로 가시길 바랍니다. 확실히 가슴 통증이 느껴진다면 (1~3) 심장 질환이 있는 확률이 훨씬 올라감을 볼 수 있습니다.

data[['cp', 'target']].groupby(['cp'], as_index=True).mean().plot.bar()

여기까지 정리 한 번 하도록 하겠습니다. 현재 나이, 성별, 가슴통증으로 심장질환의 비율과 수를 지켜보았습니다. 성별, 가슴통증으론 확실히 심장질환에 대한 output을 볼 수 있었지만, 나이로는 그 예측을 조금하기 힘이 들어보입니다. 그렇기에 Machine Learning features를 선택할 때 성별과 가슴 통증은 꼭 사용하는 것이 좋은 판단인거 같습니다.

 

계속해보도록 하겠습니다. 심장 질환과 직접적으로 연관이 있을 거 같은 혈압을 분석해보겠습니다.

fig, ax = plt.subplots(1, 1, figsize=(18, 8))
sns.kdeplot(data[data['target']==1]['trestbps'], ax=ax)
sns.kdeplot(data[data['target']==0]['trestbps'], ax=ax)
plt.legend(['target==1', 'target==0'])
plt.show()

신기합니다? 심장 질환은 혈압이 높을수록 생기는 것인줄 알았는데, 데이터 상으론 아니라고 말하고 있습니다. 그렇다면 심장 질환이 있을 때와 없을 때의 평균 혈압을 고려해보면서 제 가설이 어떤지 다시 한 번 확인하겠습니다.

fig, ax = plt.subplots(1, 1, figsize=(8, 8))

data[['trestbps','target']].groupby(['target'],as_index=True).mean().plot.bar(ax=ax)

오잉 고혈압일수록 심장질환이 높을 것이라는 제 가설이 비껴나갔습니다. 심장 질환이 없을 때의 평균 혈압이 더 높게나왔습니다. 저는 이 부분에서 데이터가 의심스럽습니다. 사실 제 코드가 의심스럽죠. 그렇다면 콜레스테롤로 넘어가볼까요? 콜레스테롤은 확실히 높으면 심장 질환이 높을거 같은 생각이 듭니다.

fig, ax = plt.subplots(1, 1, figsize=(8, 8))

data[['chol', 'target']].groupby(['target'], as_index=True).mean().plot.bar(ax=ax)

이거.. 좀 난감한 상황이 왔습니다. 콜레스테롤이 높으면 당연히 심장질환의 수가 높을 것이라는 저의 생각이 틀렸다는 것을 증명하는 데이터를 받았습니다. 사실 알고보니 이 데이터가 좀 오래되고 작아서 단순한 모델을 세우는덴 좋지만, 정확하지 않다고 많은 Kernels에서 말을 하고 있습니다. 그렇기에 이 데이터를 전적으로 신뢰하는 것은 잘 못 된 것이라고 생각하네요.

그래도 우리는 이 데이터로 계속해서 분석과 모델링을 해보도록 하겠습니다.

전체적으로 모든 것을 분석하기엔 시간도 있으니 눈에 띄는 데이터들만 분석을 해보겠습니다. (내 입맛대로)

pd.crosstab(data.slope, data.target).plot(kind='bar', figsize=(15, 6), color=['#DAF7A6', '#FF5733'])
plt.title('Heart Disease Frequency for Slope')
plt.xlabel('The Slope of The Peak Exercise ST Segent')
plt.xticks(rotation = 0)
plt.ylabel('Frequency')
plt.show()

slope라는 데이터는 the slope of the peak exercise ST segment라고 합니다. 구글에서는 급성심근경색이라고 하는데, 정확히는 ST 분절이라고 합니다. 제 생각엔 급성심근경색을 일으키는 요소인거 같습니다. 그 피크 점이 숫자가 높을수록 심각하다는 소리 같은데 2일 때 급격하게 Heart disease Frequency가 올라간 것을 볼 수 있습니다. 한번 각 수치마다의 평균을 내보도록 할까요?

data[['slope', 'target']].groupby('slope', as_index=True).mean().plot.bar()

확실히 2일 때가 가장 높은 것을 보셨죠?

이제 fbs(fasting blood sugar, 공복혈당)라는 데이터로 분석을 해봅시다. fbs는 120mg/dl 보다 높으면 1이고 낮으면 0 인데, 높으면 당뇨병에 걸렸다는 근거가 된다고 합니다. 당연히 높으면 안좋겠죠?

pd.crosstab(data.fbs, data.target).plot(kind='bar', figsize=(15,6), color=['#DAF7A6', '#FF5733'])
plt.title('Heart Disease Frequency According To FBS')
plt.xlabel('FBS - (Fasting Blood Sugar > 120 mg/dl) (1 = true; 0 = false)')
plt.xticks(rotation = 0)
plt.legend(["Haven't Disease", "Have Disease"])
plt.ylabel('Frequency of Disease or Not')
plt.show()

생각보다 FBS가 심장질환과 크게 상관 없는거 같습니다. 평균을 내보도록 합시다.

여기까지 데이터를 분석 해봤는데, 다음엔 one-hot-coding을 위한 dummy variables로 바꾸고 model을 세워서 Logistic Regression를 실행해보도록 합시다.


이상 Kaggle - Heart Disease Dataset (1) 였습니다. ^_^

반응형
LIST