본문 바로가기
머신러닝/Logistic Regression

Logistic Regression - PIMA 인디언 당뇨병 예측

by 미생22 2024. 5. 4.
728x90

PIMA 인디언은 멕시코와 미국에 걸쳐살고있던 인디언 부족이라고 합니다. 1950년대까지 PIMA 인디언은 당뇨가 없었습니다. 그런데 20세기 말, 단 50년만에 인구의 50%가 당뇨에 걸렸고 50년만에 50%의 인구가 당뇨에 걸렸습니다.

 

원래 데이터는 Kaggle에 있습니다. 이 데이터를 pinkwink github에서 가져오겠습니다.

 

각 컬럼에 대해 이야기하자면 다음과 같습니다.

 

import pandas as pd

PIMA_url = 'https://raw.githubusercontent.com/PinkWink/ML_tutorial/master/dataset/diabetes.csv'
pima = pd.read_csv(PIMA_url)
pima.head()

 

pima 데이터를 좀 살펴보겠습니다.

pima.info()

 

총 768개의 데이터가 있고 전부 수치형 데이터네요.
안전하게 데이터를 처리하기 위해 전부 float형태로 바꿔보겠습니다.

 

pima = pima.astype('float')
pima.info()

 

머신러닝 전에 각 컬럼별 상관관계를 확인해보겠습니다.

 

pima.corr()

저번시간처럼 이걸 heatmap으로 보기쉽게 그려볼게요

 

import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline #주피터 노트북에서 주로 쓰는 코드

plt.figure(figsize=(12, 10))
sns.heatmap(pima.corr(), annot=True, cmap='YlGnBu')
plt.show()

outcome과 관련있는 것들을 찾아보면 pregnancies, glucose, BMI, Age 정도가 있네요... 높은 수치는 아니지만...
그런데 0이 있습니다.

 

pima==0

(pima==0).astype(int)

이렇게하면 false는 0, true는 1이 됩니다.

 

 

이거를 sum시켜보겠습니다. 그러면 true의 갯수가 나오겠죠.

 

(pima==0).astype(int).sum()

이 0은 null은 아니라서 위 코드가 생각보다 자주 쓰일겁니다.
그런데 혈압이 0인 데이터, 체지방 지수가 0인 데이터가 있다고합니다.
절대로 0이 될 수 없는 데이터를 가져와보겠습니다.
null값이라면 앞뒤 데이터의 중간값이나 부드럽게 넘어갈 수 있는 데이터로 가져오겠지만 0은 수치라서 상당히 힙듭니다.
특히 의학적 지식과 PIMA 인디언에 대한 정보가 없으므로 일단 평균값으로 대체하겠습니다.

 

zero_feature = ['Glucose', 'BloodPressure', 'SkinThickness', 'BMI']
pima[zero_feature] = pima[zero_feature].replace(0, pima[zero_feature].mean()) #이렇게하면 해당 열의 평균값으로 대체합니다.
(pima==0).astype(int).sum()

파이썬의 좋은점이죠. 열 전체를 replace 시킬 수 있고 replace 대상에 한줄의 코드만으로 각 열마다의 평균값을 넣을 수 있습니다.

 

이제 데이터를 나누겠습니다.

from sklearn.model_selection import train_test_split

X = pima.drop(['Outcome'], axis=1)
y = pima['Outcome']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, 
                                                    #당뇨냐 아니냐에 대한 구조를 그대로 가져가라고 하겠습니다.
                                                    stratify=y,
                                                    random_state=13)

 

여기서 저번시간에 썼던 stratify를 적용해보겠습니다. train과 test에 당뇨냐 아니냐에 대한 비율을 그대로 가져가라고 하는 것입니다.

 

*strafity란?
stratify 매개변수는 데이터를 분할할 때, 분할된 데이터셋이 원본 데이터셋의 클래스 비율을 유지하도록 보장하는 역할을 합니다.이 매개변수를 사용하면 훈련 및 테스트 데이터셋이 원본 데이터셋과 동일한 클래스 비율을 갖게 됩니다. 이는 특히 클래스가 불균형하게 분포되어 있을 때 중요합니다. 예를 들어, 100개의 샘플이 있는데 90개가 클래스 A에, 10개가 클래스 B에 속한다고 가정해 봅시다. 이런 경우, stratify=y를 사용하여 데이터를 분할하면 훈련 및 테스트 세트 각각에 클래스 A와 클래스 B의 비율이 유지됩니다.

 

pipeline을 만들어주겠습니다. 저번시간처럼 Standard Scaler를 쓰겠습니다.

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression

estimate = [
    ('scaler', StandardScaler()),
    ('clf', LogisticRegression(solver='liblinear', random_state=13))
]

pipe_lr = Pipeline(estimate)
pipe_lr.fit(X_train, y_train)

fit시켜주고, predict 시키겠습니다. 이번에는 test 데이터만 해보겠습니다.

pred = pipe_lr.predict(X_test)

 

이제 model evaluation때 배웠던 수치들을 전부 확인해보겠습니다.

from sklearn.metrics import accuracy_score, recall_score, precision_score, roc_auc_score, f1_score

print('Accuracy : ', accuracy_score(y_test, pred))
print('Recall : ', recall_score(y_test, pred))
print('Precision : ', precision_score(y_test, pred))
print('AUC score : ', roc_auc_score(y_test, pred))
print('f1 score : ', f1_score(y_test, pred))

그러나 이 수치들은 수치 자체를 평가할 순 없습니다.
총 컬럼이 8개였으므로 x1 x2 x3, ..., x8 이고 theta1, theta2, ..., theta8으로 총 8개의 변수가 사용된겁니다.
어떤 변수가 가중치가 큰지 확인해보기 위해서 pipe_lr의 clf의 coef_를 확인해보겠습니다.

 

pipe_lr['clf'].coef_

list가 두개네요... [0]을 붙여서 때고 column이름을 붙여줘야겠습니다.

coef = list(pipe_lr['clf'].coef_[0])
label = list(X_train.columns)
coef

 

이제 중요한 feature에 대해 map으로 그려보겠습니다.

 

features = pd.DataFrame(columns=label, data=coef) 이건 안되네요..list는 열에만 넣어집니다.
그래프에 중요도 순서대로 나타나기 위해 importances 순으로 나열해보겠습니다.

 

features = pd.DataFrame({'Features':label, 'importance':coef})
features.sort_values(by=['importance'], ascending=True, inplace=True)
features

그래프에 color을 입히기 위해 column을 만들어보겠습니다.

 

features['positive'] = features['importance']>0
features

그래프에 쉽게 표기하기 위해 set_index 시켜줍니다.

features.set_index('Features', inplace=True)
features

 

이제 map을 그려보겠습니다.

features['importance'].plot(kind='barh',
                            figsize=(11,6),
                            color=features['positive'].map({True:'blue', False:'red'}) #barh에 color를 부여하기 위한 옵션입니다.
                            )
plt.xlabel('importance')
plt.show()

네, 포도당, BMI등은 당뇨에 영향을 미치는 정도가 높습니다.
혈압은 예측에 부정적인 영향을 줍니다.
연령이 BMI보다 출력 변수와 더 관련되어 있었지만, 모델은 BMI와 Glucose에 더 의존합니다.

728x90