파이썬/머신러닝

[머신러닝] KNN을 이용한 비만도 분석

취준생코린이 2021. 6. 7. 11:19
728x90

import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris # sklearn에서 제공하는 붓꽃 데이터
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import LinearRegression

 

1. 문제정의

  • 500명의 키와 몸무게, 비만도 라벨을 통해서 비만을 판단하는 모델을 만들어보자2. 데이터 수집

 

2. 데이터 수집

data = pd.read_csv('bmi_500.csv',
           index_col = "Label" #Label컬럼을 인덱스로 설정)

 

3.데이터 전처리

info()로 데이터의 대략적인 요약을 확인하고 describe()로 기술통계를 확인하여 이상치 유무를 확인한다.

data.info()

#기술통계 확인
data.describe()
# 여기서는 이상치가 없는것을 확인 할 수 있다.

# 이상치 범위
# IQR = 3사분위 - 1사분위
# 1사분위 - 1.5*IQR보다 작고
# 3사분위 + 1.5*IQR보다 크면
# 이상치 이다.

 

# data.index는 모든 인덱스를 보여줌
# unique()는 중복되는 값을 뺴고 유일한 값만 보여줌
# 따라서 data.index.unique()는 인덱스의 종류를 볼 수 있다.
data.index.unique()

 

4. 탐색적 데이터 분석(EDA)

  • 각 비만도를 등급별로 시각화해보자
# 시각화
dataObesity = data.loc['Obesity']
dataNormal = data.loc['Normal']
dataWeak = data.loc['Weak']
dataOverweight = data.loc['Overweight']
dataExtremeObesity = data.loc['Extreme Obesity']
dataExtremelyWeak = data.loc['Extremely Weak']

plt.scatter(dataObesity['Weight'], # x축 데이터
           dataObesity['Height'], # y축 데이터
           c = "red",
           label = "Obesity")

plt.scatter(dataNormal['Weight'], # x축 데이터
           dataNormal['Height'], # y축 데이터
           c = "blue",
           label = "Normal")

plt.scatter(dataWeak['Weight'], # x축 데이터
           dataWeak['Height'], # y축 데이터
           c = "green",
           label = "Weak")

plt.scatter(dataOverweight['Weight'], # x축 데이터
           dataOverweight['Height'], # y축 데이터
           c = "yellow",
           label = "Overweight")

plt.scatter(dataExtremeObesity['Weight'], # x축 데이터
           dataExtremeObesity['Height'], # y축 데이터
           c = "Magenta",
           label = "Extreme Obesity")

plt.scatter(dataExtremelyWeak['Weight'], # x축 데이터
           dataExtremelyWeak['Height'], # y축 데이터
           c = "cyan",
           label = "Extremely Weak")

plt.legend(loc = 'upper right') # 우측 상단에 label 띄우기
plt.legend(loc = (1.0, 0))  # 위치를 직접지정

plt.show()

 

위에서 scatter로 각 비만도 별로 색깔을 나눠서 그래프를 그렸는데

반복되는 코드가 너무 많은 것을 확인 할 수 있다.

함수를 만들어서 이를 간단히 바꿔보자.

 

def drawScatter(row, color):
    plt.scatter(data.loc[row]['Weight'], # x축 데이터
           data.loc[row]['Height'], # y축 데이터
           c = color, label = row)
plt.figure(figsize=(8,8)) # 그래프 크기
labelName = data.index.unique()
# color = ['red', 'coral', 'yellow', 'yellowgreen', 'green', 'c']
color = ['green', 'yellow', 'yellowgreen', 'c', 'coral', 'red']
for row,color in zip(labelName, color):
    drawScatter(row, color)

plt.legend(loc = (1.0, 0))  # label의 위치를 직접지정
plt.xlabel('Weight')
plt.ylabel('height')
plt.show()

 

5. 모델선택 및 하이퍼 파라미터 튜닝

# 문제/ 정답 데이터 나누기
data = pd.read_csv('bmi_500.csv')
print(data.head())
# Height, Weight -> 문제
# Label -> 정답
X = data.loc[:, ["Height", "Weight"]]
y = data.loc[: ,"Label"]

print(X.shape)
print(y.shape)

 

# 훈련 / 평가 데이터 나누기
X_train, X_test, y_train, y_test = train_test_split(X,y,random_state=3,
                                                    test_size = 0.3)
                                                    
# KNN모델 사용
knn = KNeighborsClassifier()

 

6. 훈련

# 훈련
knn.fit(X_train, y_train)

 

7. 평가

# 평가
print(f"train score: {knn.score(X_train, y_train)}" +
      f"\ntest scor: {knn.score(X_test, y_test)}")

예측하기

# 예측하기
# 문제 데이터인 X에 값을 2개 넣었으니 예측을 할때도 X를 2개씩 넣어야 한다.
knn.predict( [[174,67],[180,80]] )

 

이웃의 개수에 따른 훈련 점수와 평가점수를 구해보자.

trainList = []
testList = []
for k in range(1,101):
    # 모델 선택 및 하이퍼 파라미터 튜닝
    model = KNeighborsClassifier(n_neighbors = k)
    # 모델학습
    model.fit(X_train, y_train)
    # 모델 평가
    trainList.append(model.score(X_train, y_train))
    print(f"train 이웃{k}개 : {trainList[k-1]}")
    testList.append(model.score(X_test, y_test))
    print(f"test 이웃{k}개 : {testList[k-1]}")

 

위에서 만든 점수를 이용해서 시각화를 해보자.

# 시각화
plt.figure(figsize=(25,5)) # 출력할 그래프 크기
plt.plot(range(1, 101), # X데이터 = 1~100
        trainList,  # y데이터
         c="red")
plt.plot(range(1, 101), # X데이터 = 1~100
        testList,  # y데이터
         c="blue")

plt.xticks(range(1,101))
plt.grid() #기준선
plt.show()

 

위에 그래프는 유클리드 거리 방식을 이용하여 구한 그래프이다.

 

이번에는 여러가지 방식을 이용하여 그래프를 구해보자.

trainList = []
testList = []
trainList2 = []
testList2 = []
trainList3 = []
testList3 = []
trainList4 = []
testList4 = []
for k in range(1,101):
    model = KNeighborsClassifier(n_neighbors = k)
    model.fit(X_train, y_train)
    trainList.append(model.score(X_train, y_train))
    testList.append(model.score(X_test, y_test))

for k in range(1,101):
    # p=1 : 맨하튼 거리 방식
    model = KNeighborsClassifier(n_neighbors = k, p=1)
    model.fit(X_train, y_train)
    trainList2.append(model.score(X_train, y_train))
    testList2.append(model.score(X_test, y_test))
    
for k in range(1,101):
    # 'distance': 거리의 역으로 가중치 포인트
    model = KNeighborsClassifier(n_neighbors = k, weights="distance")
    model.fit(X_train, y_train)
    trainList3.append(model.score(X_train, y_train))
    testList3.append(model.score(X_test, y_test))
    
for k in range(1,101):
    # 'brute'는 무차별 대입 검색을 사용
    model = KNeighborsClassifier(n_neighbors = k, algorithm='brute') 
    model.fit(X_train, y_train)
    trainList4.append(model.score(X_train, y_train))
    testList4.append(model.score(X_test, y_test))

# 한글 폰트 사용
from matplotlib import rc
rc('font',family='Malgun Gothic')
# 시각화
plt.figure(figsize=(30,10)) # 출력할 그래프 크기
plt.plot(range(1, 101), trainList, c="red", label = "train")
plt.plot(range(1, 101), testList, c="blue", label = "test")
plt.plot(range(1, 101), trainList2, c="red", linestyle=':', label = "train맨하튼")
plt.plot(range(1, 101), testList2, c="blue", linestyle=':', label = "test맨하튼")
plt.plot(range(1, 101), trainList3, c="red", linestyle='--', label = "trainDistance")
plt.plot(range(1, 101), testList3, c="blue", linestyle='--', label = "testDistance")
plt.plot(range(1, 101), trainList4, c="coral", linestyle='solid', label = "trainBrute")
plt.plot(range(1, 101), testList4, c="yellowgreen", linestyle='solid', label = "testBrute")

plt.legend(loc = (1.0, 0)) 

plt.xticks(range(1,101))
plt.grid() #기준선
plt.show()

 

 

아까 위에서 비만도를 등급별로 구별한 그래프위에 선형회귀선을 구해보자.

def drawScatter(row, color):
    plt.scatter(data.loc[row]['Weight'], data.loc[row]['Height'],
           c = color, label = row)

def drawPlot(row):
#     plt.plot(data.loc[row]['Weight'], data.loc[row]['Height'],"o")
    plt.plot(data.loc[row]['Weight'],model.predict(data.loc[row]["Weight"].values.reshape(-1,1)))
def modelFit(row):
    model.fit(data.loc[row]["Weight"].values.reshape(-1,1), data.loc[row]["Height"])
# 선형회귀 그래프 그리기
# Weight x축, Height y축
data = pd.read_csv('bmi_500.csv',index_col = "Label")
plt.figure(figsize=(8,8))
    
labelName = data.index.unique()
color = ['green', 'yellow', 'yellowgreen', 'c', 'coral', 'red']
for row,color in zip(labelName, color):
    drawScatter(row, color)

# 선형회귀선
model = LinearRegression()
for row in labelName:
    modelFit(row)
    drawPlot(row)

plt.legend(loc = (1.0, 0))  # label의 위치를 직접지정
plt.xlabel('Weight')
plt.ylabel('height')
plt.show()

728x90