codestates / ds-blog

blog sharing for data science bootcamp course
2 stars 4 forks source link

[이재우] section01 solo project #141

Open jingwoo4710 opened 4 years ago

jingwoo4710 commented 4 years ago

1. Research Question

잠깐 잠잠했던 확산을 뒤로하고, 다시금 COVID-19의 확산이 활발히 진행되고 있음에 따라, 전 세계가 코로나바이러스 확산을 최소화 하기 위해 최선을 다하고 있다. 확산을 최소화 하기 위해서는 정확한 요인을 파악하여 대처해야 한다. 캐글(Kaggle)에서 COVID19 Global Forecasting (Week 1) 같은 Challenge를 발표한 사례도 있다. 내가 주목한 부분은 국가별로 확진자수가 다르다는 점이다. 물론 국가별로 다를 수 있는 요소들에 다양할 수가 있지만, 예를 들어 국가별 대응 정책, 국민의 성향, 정부와의 신뢰관계 등이 있다. 내가 선택한 요인으로는 국가별 성향이 코로나바이러스 확산과 관계가 있을 수 있다고 생각했다. 따라서, K-means clustering을 활용하여 비슷한 성향을 가진 국가들의 코로나 확진자수의 관계를 확인해보자.

1.1 Big Five Personality Test

국가별 성향을 확인하기위해서 캐글에 있는 빅 파이브 성격테스트를 사용하였다. 한국을 예를 들면, 다음과 같다. 캐글에 빅 파이브 성격테스트에서 한국인 국적을 가진 사람들의 데이터를 모아서, 평균을 내어 한국의 성향으로 분석을 하였다. 빅 파이브 성격테스트에 대해서 간단히 알아보면, 기본적으로 설문지에 대답하는 형식으로 이루어져 있으며, 각 질문에 1점 부터 5점 까지의 범위로 완전 동의하면 5점, 그 반대는 1점을 나타낸다. 질문들은 빅 파이브 성격테스트에서 분류한 각 5가지의 성향을 확인 할 수 있는 질문으로 이루어져 있다. 결과적으로 설문지에 대한 답변의 총 합을 통해 성격을 파악 할 수 있다. 다섯가지의 성향은 신경성(N; Neuroticism), 외향성(E; Extraversion), 개방성(O; Openness to Experience), 우호성(A; Agreeableness), 성실성(C; Conscientiousness)으로 분류된다.

2. Data

다음의 내용은 데이터의 출처와 간략한 설명을 포함하고 있다.

  1. COVID-19 위의 데이터는 WHO Coronavirus Disease (COVID-19) Dashboard에서 최신의 코로나바이러스 데이터를 얻기 위해 사용하였다.
  2. Big Five Personality Test 위의 데이터는 전 세계의 1,015,342명의 빅 파이브 성격테스트 설문지에 대한 답을 기록하고 있다.
  3. Countries ISO Codes 위의 데이터는 나라를 나타낼 수 있는 다양한 코드들을 담고있다.
    • Alpha-2 code: 2개의 알파벳으로 구성되어진 Country Code
    • Alpha-3 code: 3개의 알파벳으로 구성되어진 Country Code
    • Numeric code: 숫자 Country Code
    • ISO 3166-2: ISO 3166-2 code. Formatted as: ISO 3166-2:[2 characters]

총 3개의 데이터를 병합하여 최종적으로 원하는 데이터 세트를 완성하는것을 목표로 하였고, 최종 데이터 세트와 함께 분석을 하였다. 그럼 그 최종 데이터 세트를 얻기 위한 과정을 함께 보자.

3. 데이터 처리

데이터 전처리를 시작하기에 앞서, 가장 먼저 해야 할 것은 데이터를 불러오는것이다. 데이터를 불러오기 위해 다양한 방법들이 있지만, 각 데이터를 포함하고 있는 웹 사이트에서 다운을 받아 로컬에 저장 후 사용하였다.

#Library
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.cluster import KMeans 
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from scipy.stats import pearsonr, spearmanr

3.1 코로나바이러스 데이터를 통해 총 확진자수 구하기

corona_df = pd.read_csv('WHO-COVID-19-global-data.csv')

WHO에서 제공하는 코로나 바이러스 데이터프레임을 같이 한 번 살펴보자.

  Date_reported Country_code Country WHO_region New_cases Cumulative_cases New_deaths Cumulative_deaths
2020-01-03 AF Afghanistan EMRO 0 0 0 0
2020-01-04 AF Afghanistan EMRO 0 0 0 0

전 세계 국가를 대상으로, 일별로 코로나 바이러스 확진자, 누적 확진자, 코로나 바이러스로 인한 사망자, 누적 사망자를 확인 할 수 있다. 제일 첫번째 행을 보면 2020년 1월 3일 아프가니스탄에서는 확진자, 누적 확진자, 사망자, 누적 사망자가 모두 0명을 알 수 있다.

다음은 현재를 기준으로 가장 많은 누적 확진자수를 보여주는 4개의 국가의 일별 트렌드를 보여준다. 다운로드

위의 그래프 처럼 국가별로 날짜, 일을 기준으로 확진자수를 확인 할 수 있었다.

데이터 전처리를 진행 하였을 때, WHO에서 제공하는 코로나 바이러스 데이터의 Header가 공백을 포함하고 있는 것을 확인 하였다. 따라서, 제일 먼저 strip()이라는 메서드를 사용하여, 공백을 제거 하였다.

# 열의 이름 공백 제거
cols = corona_df.columns.to_list()
corona_df.columns = [element.strip() for element in cols]

분석의 목표는 국가 성향별, 확진자수의 많고 적음을 확인하고 싶은 것이기 때문에, 국가별 총 확진자수가 필요하다. 그래서, 위의 데이터 세트에서 New_cases를 활용하여 국가별로 groupby하여 총합을 구하는 방식으로 활용하였다.

# 국가별 총 확진자수
df_covid = corona_df.groupby(['Country', 'Country_code'])\
                    .sum()\
                    .rename_axis(['Country', 'Country_code'])\
                    .reset_index()
df_covid = df_covid.iloc[:,:3]
df_covid.columns = ['국가', '국가코드', '확진자 수']
df_covid.head()
  국가 국가코드 확진자 수
Afghanistan AF 39422
Albania AL 14410
Algeria DZ 52270
American Samoa AS 0
Andorra AD 2370

마지막으로, Header의 이름을 보기 편하게 바꾸어 주었다.

3.2 빅 파이브 성격테스트 결과

빅 파이브 성격테스트의 데이터의 전처리를 시작해보자. 먼저 raw data의 생김새를 확인해보자.

  EXT1 EXT2 EXT3 EXT4 EXT5 EXT6 EXT7 EXT8 EXT9 EXT10 country
4.0 1.0 5.0 2.0 5.0 1.0 5.0 2.0 4.0 1.0 GB
3.0 5.0 3.0 4.0 3.0 3.0 2.0 5.0 1.0 5.0 MY
2.0 3.0 4.0 4.0 3.0 2.0 1.0 3.0 2.0 5.0 GB
2.0 2.0 2.0 3.0 4.0 2.0 2.0 4.0 1.0 4.0 GB
3.0 3.0 3.0 3.0 5.0 3.0 3.0 5.0 3.0 4.0 KE

위의 데이터 프레임을 보는것과 같이 각 Header가 성향 + 질문 Number로 구성되어 있다. 예를 들어, EXT1은 외향성을 확인하는 첫번째 질문인 것이다. 그리고, 답변자의 국적을 확인할 수 있었다. 따라서 다음과 같은 전처리 과정을 생각 할 수 있다.

  1. 각 성향을 확인하는 질문의 총합을 구한다.

  2. 국가별로 groupby하여 평균을 낸다.

그럼 시작해보자.

# 빅 파이브 성격테스트에서 Postive 와 Negative 문제 나누기 + 필요한 열 정의
pos_questions = [
    'OPN1','OPN3','OPN5','OPN7','OPN8','OPN9','OPN10',        # 7 개방성
    'CSN1','CSN3','CSN5','CSN7','CSN9','CSN10',               # 6 성실성
    'EXT1','EXT3','EXT5','EXT7','EXT9',                       # 5 외향성
    'AGR2','AGR4','AGR6','AGR8','AGR9','AGR10',               # 6 친화성
    'EST1','EST3','EST5','EST6','EST7','EST8','EST9','EST10', # 8 안정성(신경성)
]

neg_questions = [
    'OPN2','OPN4','OPN6',                # 3 개방성
    'CSN2','CSN4','CSN6','CSN8',         # 4 성실성
    'EXT2','EXT4','EXT6','EXT8','EXT10', # 5 외향성
    'AGR1','AGR3','AGR5','AGR7',         # 4 친화성
    'EST2','EST4',                       # 2 안정성(신경성)
]

use_cols = pos_questions + neg_questions + ['country']
trait = pd.read_csv('data-final.csv', sep = '\t', usecols=use_cols)

나중의 합계를 계산하기 위해서 부정적인 질문과 긍정적인 질문을 분류

  EXT1 EXT2 EXT3 EXT4 EXT5 EXT6 EXT7 EXT8 EXT9 EXT10 country
4.0 1.0 5.0 2.0 5.0 1.0 5.0 2.0 4.0 1.0 GB
3.0 5.0 3.0 4.0 3.0 3.0 2.0 5.0 1.0 5.0 MY
2.0 3.0 4.0 4.0 3.0 2.0 1.0 3.0 2.0 5.0 GB
2.0 2.0 2.0 3.0 4.0 2.0 2.0 4.0 1.0 4.0 GB
3.0 3.0 3.0 3.0 5.0 3.0 3.0 5.0 3.0 4.0 KE

각 성향별로 모든 문제의 총 합을 구하기 위해서는 결측치가 없어야 한다. 데이터를 보면, NaN값은 없는데 0으로 대답한 경우를 알 수 있다. 따라서, 0점으로 대답을 한 경우 결측치 처리를 하고, 모든 결측치를 제외하자. 또, 빅 파이브 성격테스트에서 국가의 이름이 None으로 설정된 경우가 있다. 이 또한 결측치 처리를 하고, 제외하자.

# 결측치 처리
trait = trait.replace(0,np.NaN).dropna(axis = 0).reset_index(drop = True)
# 국가명이 None 인 경우 삭제
i = trait.loc[trait.country == 'NONE'].index
trait.drop(i, inplace= True)

답이 0인 경우 결측치로 분류, 국가이름이 None인 경우 결측치 처리.

여기서 중요한 가정이 설립된다. 먼저, 국민들의 성향으로 국가를 대표하기 위해서는 충분한 샘플 사이즈가 필요하다. 그렇지 않게 되면, 극단적으로 국민 1명이 국가를 대표하게 되면서 엄청나게 편향된 정보를 주기 때문이다. 따라서, 충분한 샘플 사이즈로 1000명으로 가정을 하여서, train data로서 나중에 분류를 학습하기 위한 데이터로 설정하였다.

# 데이터의 크기가 충분한 국가만 추출
train_countries = pd.DataFrame(data = trait.groupby('country')\
                                           .agg('size')\
                                           .loc[trait.groupby('country').agg('size') > 1000])

train_countries = train_countries.index.to_list()

나중에 K-means clustering을 Training을 하기위한 데이터를 만들기 위해 샘플 사이즈가 충분해 그 나라를 대표할 수 있는경우

추가적으로, 몇 개의 국가들은 990개 980개등 수치적으로 1000개의 샘플 사이즈와 근접한 국가들이 존재하였다. 그 국가들은, 모델의 성능 또는 결과를 테스트하기 위해서 test data로 정의 하였다. 먼저 1000개의 샘플 사이즈는 넘지 않는 국가들로 데이터를 구성하여, 그 중에서 500개의 이상의 샘플 사이즈를 가지도록 test data를 정의하였다.

# train 데이터와 test 데이터 
train = trait.loc[trait.country.isin(filtered_countries)].reset_index()
test = trait.loc[~trait.country.isin(filtered_countries)].reset_index(drop = True)
# test 데이터 또한 샘플이 최소 500개는 가지도록 
test_countries = test.groupby('country').size()\
                     .loc[test.groupby('country').size() > 500]\
                     .index.to_list()

test = test.loc[test.country.isin(test_countries)]

test 할 나라들도 최소 500개의 샘플 데이터는 가지도록 분류

합계를 내어, 평균을 계산하기 위해서는 점수 스케일 작업이 필요하다. 예를 들어, 외향성을 확인하는 첫번째 질문이 파티를 좋아합니까?일 경우, 5점은 굉장한 외향성을 나타낸다고 볼 수 있는 반면에, 외향성을 확인하는 두번째 질문은 나는 말을 많이 하는편이 아니다.이다. 이 질문에서 5점은 외향성의 반대의 성향을 크게 나타낸다. 따라서, 앞에서 정의 했던바와 같이 긍정적인 질문과 부정적인 질문을 나누어서, 부정적인 질문들의 점수6을 빼주어서 위의 나는 말을 많이 하는편이 아니다에서 1점이 될 수 있도록 점수 스케일을 통일 시켜주었다.

# 평균 계산을 위해 negative question 점수 scale 바꾸기
train[neg_questions] = train[neg_questions].apply(lambda x: 6 - x)
test[neg_questions] = test[neg_questions].apply(lambda x: 6 - x)

부정적인 질문들의 점수 Scale을 바꿈


# 설문지 결과 Table
df_train = train.copy()
df_test = test.copy()

결측치도 제거하였고, 점수 스케일링도 통일 시켜주었기 때문에, 합한 다음 평균을 내보자. 먼저 같은 성향을 가진 점수들끼리 합하는 코드를 list comprehension을 통해서 만들었다. 그런다음 열 합산을 통하여 합산 하였다.

같은 성향을 파악하는 질문의 합을 구하는 함수

def summing_axis (df): traits = ['OPN','CSN','EXT','AGR','EST'] for trait in traits: df[trait] = df[[col for col in df.columns if trait in col]].sum(axis = 1) return df[traits+['country']]

df_train = summing_axis(df_train) df_test = summing_axis(df_test)

마지막으로, `groupby`를 통해 국가별로, `mean`을 통해 평균을 구한 다음 편의를 위해 header 또한 다 바꾸어 주었다.
```py
# 나라별로 train data mean 구하기
mean_train = df_train.groupby('country').mean().rename_axis('Code').reset_index()
mean_train.columns = ['국가코드', '개방성', '성실성', '외향성', '우호성', '안정성']
  국가코드 개방성 성실성 외향성 우호성 안정성
AE 37.639791 34.032090 30.261933 37.917770 31.475732
AR 40.133075 31.678530 28.345068 36.416248 32.381431
AT 40.836251 32.518538 29.899588 36.876931 29.856334
AU 38.551472 33.626061 29.937915 37.918328 30.393272
BE 39.308450 32.029262 29.967453 37.661989 30.598089

test data 또한 같은 방법으로 적용한 결과는 다음과 같다.

# 나라별로 test data mean 구하기
mean_test = df_test.groupby('country').mean().rename_axis('Code').reset_index()
mean_test.columns = ['국가코드', '개방성', '성실성', '외향성', '우호성', '안정성']
  국가코드 개방성 성실성 외향성 우호성 안정성
CR 39.633028 32.664220 28.759633 35.908257 31.051376
EC 38.383138 31.910352 29.119530 35.337247 31.806830
EE 38.964045 32.362921 28.283146 35.783146 30.279775
EG 37.943890 33.007481 28.480050 36.966334 33.011222
IS 39.057728 32.614525 30.225326 37.696462 30.463687

3.3 데이터 병합

앞에서 코로나바이러스 데이터를 통한 총 확진자수를 빅 파이브 성격테스트의 결과와 국가코드를 통하여 병합 해주었다.

# train 데이터 필터링
train_covid = df_covid.loc[df_covid['국가코드'].isin(train_countries)].reset_index(drop = True)

# 빅 파이브 성격테스트와 COVID19 데이터 합치기
df_train = train_covid.merge(mean_train, on = '국가코드')
  국가 국가코드 확진자 수 개방성 성실성 외향성 우호성 안정성
Argentina AR 798486 40.133075 31.678530 28.345068 36.416248 32.381431
Australia AU 27149 38.551472 33.626061 29.937915 37.918328 30.393272
Austria AT 49886 40.836251 32.518538 29.899588 36.876931 29.856334
Belgium BE 132109 39.308450 32.029262 29.967453 37.661989 30.598089
Brazil BR 4915289 40.536946 31.240467 26.505017 34.946725 31.545703

국가별 성향 + 총 확진자수를 포함한 최종 데이터 세트를 만들어 볼 수 있었다. 이제 완성된 train data를 통하여, K-means clustering 모델을 학습 시킨 후, 그를 바탕으로 test data를 적용하여 그 결과도 같이 확인 해보도록 하자.

3.4 Clustering

K-means clustering 모델을 설정하기 위해서, hyper parameter인 k를 미리 설정해야 한다. k를 설정하기 위해서 2가지 방법을 사용하였다. 먼저 elbow plot을 통해서, 적절한 k를 확인하여 범위를 좁혔다. 범위를 좁힌 k의 값들로 모델에 적용하여, 클러스터링 결과를 확인해 보고 각 클러스터별 분포가 고르게 되었는지도 확인하여 최종 k를 결정하였다. 이 모든 과정의 결과로 k는 2로 설정하였다.

# elbow plot
distortions = []
K = range(1,10)
for k in K:
    kmeanModel = KMeans(n_clusters=k)
    kmeanModel.fit(df_train.iloc[:,3:8])
    distortions.append(kmeanModel.inertia_)

plt.figure(figsize=(16,8))
plt.plot(K, distortions, 'bx-')
plt.xlabel('k')
plt.ylabel('Distance')
plt.title('The Elbow Plot')
plt.vlines(x=3, ymin=0, ymax=300, alpha=0.5, color='blue', linestyle='dashed', linewidth=1.0)
plt.show()

Elbow

팔꿈치 처럼 꺾이는 곳은 k=3 이다. 하지만, clustering 결과 후 분포도 중요하다.


# k에 따른 분포 비교

for k in range(2,5): kmeanModel = KMeans(n_clusters=k) kmeanModel.fit(df_train.iloc[:,3:8]) dftrain['군집'] = kmeanModel.labels print('k = ', k) print(df_train['군집'].value_counts(),'\n')

k =  2
0    26
1    24
Name: 군집, dtype: int64 

k =  3
1    22
2    16
0    12
Name: 군집, dtype: int64 

k =  4
0    22
2    13
1     9
3     6
Name: 군집, dtype: int64 
> 가장 고르게 분포된, k=2 설정

```py
#k = 2로 설정
kmeans = KMeans(n_clusters=2)
kmeans.fit(df_train.iloc[:,3:8])
df_train['군집'] = kmeans.labels_
  국가 국가코드 확진자 수 개방성 성실성 외향성 우호성 안정성 군집
Argentina AR 798486 40.133075 31.678530 28.345068 36.416248 32.381431 1
Australia AU 27149 38.551472 33.626061 29.937915 37.918328 30.393272 0
Austria AT 49886 40.836251 32.518538 29.899588 36.876931 29.856334 0
Belgium BE 132109 39.308450 32.029262 29.967453 37.661989 30.598089 0
Brazil BR 4915289 40.536946 31.240467 26.505017 34.946725 31.545703 1

여기서 미국과 인도의 확진자수는 다른 국가들에 비해, 상대적으로 엄청나게 많은 확진자수를 보유하기 때문에 좀 더 일반적인 결과를 도출 하기위해서, 둘 국가는 oulier로 제외하였다.

# 인도와 미국의 확진자수를 Outlier 취급

i = df_train.loc[(df_train['국가'] == 'India') | (df_train['국가'] == 'United States of America')].index

df_train.drop(i, inplace=True)

최종적으로, 클러스터링의 결과로 groupby한 다음, mean을 취해주어서, 클러스터 별 평균 확진자수와 평균 성향을 알아 볼 수 있는 최종결과를 확인 할 수 있었다.

# 군집별 분류

traits = ['개방성','신경성','외향성','우호성','안정성']

train_res = df_train.iloc[:, 2:]\
                    .groupby('군집')\
                    .mean()\
                    .rename_axis('군집')\
                    .reset_index()
train_res = train_res.apply(lambda x: x.round(2))
train_res = train_res.sort_values('확진자 수', ascending=False).reset_index(drop=True)
  군집 확진자 수 개방성 성실성 외향성 우호성 안정성
1 512379.08 39.65 32.13 28.48 35.84 31.62
0 168060.58 38.62 33.39 29.80 37.43 30.49

train data를 통해서 학습 되어진 모델에 test data를 적용시켜 보았다. train data와 같은 방법으로 최종결과를 확인 할 수 있었다.

3.5 Test

# test 나라들의 데이터 예측
mean_test['군집'] = kmeans.predict(mean_test.iloc[:,1:])
  국가코드 개방성 성실성 외향성 우호성 안정성 군집
CR 39.633028 32.664220 28.759633 35.908257 31.051376 1
EC 38.383138 31.910352 29.119530 35.337247 31.806830 1
EE 38.964045 32.362921 28.283146 35.783146 30.279775 1
EG 37.943890 33.007481 28.480050 36.966334 33.011222 1
IS 39.057728 32.614525 30.225326 37.696462 30.463687 0
# 군집별 분류
df_test = mean_test.merge(df_covid, on = '국가코드')

test_res = df_test.loc[:, ['군집', '확진자 수'] + traits]\
                    .groupby('군집')\
                    .mean()\
                    .rename_axis('군집')\
                    .reset_index()
test_res = test_res.apply(lambda x: x.round(2))
test_res = test_res.sort_values('확진자 수', ascending=False).reset_index(drop=True)
  군집 확진자 수 개방성 성실성 외향성 우호성 안정성
1 100305.4 39.24 32.40 28.40 35.64 31.53
0 32628.0 38.57 34.19 29.64 37.93 30.71

train data와 test data의 결과를 비교해보니, 꽤나 좋은 성능을 확인 할 수 있었다. 먼저 확진자수가 군집 1에서 평균적으로 높음을 알 수 있었다. 그리고 군집 1의 성향은 개방성이 군집 0보다 약 1점가량 높게 나왔음을 알 수 있다. 반면에 우호성은 군집 0에 비해 상대적으로 낮음을 알 수 있었다. 개방성은 간단히 설명하면, 야외 엑티비티를 좋아하는 성향으로 볼 수 있는데 그 결과로 군집 1의 확진자수가 높음을 예상할 수 있었다. 반면에 개방성도 높지만 우호성이 높은 군집 0의 경우 좀 더 주어진 환경에 잘 적응함을 나타내는데, 비록 야외 활동을 좋아하지만 코로나 바이러스로 바뀐 문화에 적응하여 다른 방식으로 야외 활동을 즐겼기 때문에 평균 확진자수가 군집 1에 비해 낮은것이 아닐까 추측할 수 있다.

4. 결과

6

위의 그래프를 통해서, Test data와 Train data의 클러스터링 결과를 확인할 수 있었다. 예상대로, Cluster 0의 확진자 수가 Cluster 1의 확진자 수보다 많음을 확인 할 수 있었고, 가장 놀라운 사실은 두 데이터 모두 Cluster 0의 확진자 수가 Cluster 1의 확진자 수의 비율이 되게 비슷하다는 것이다. 물론, 이 결과를 통해서 코로나 확산의 요인 중의 하나는 빅 파이브 성격테스트 이다라고 말 할 순 없다. 이 모든 결과들 또한 운이 좋아 그렇게 나온 것일 수 있다. 하지만, 그래도 이 프로젝트를 통해서 나라의 성향을 파악할 수 있고, 코로나바이러스의 확산의 요인중에 하나 일 수 있지 않을까 라는 생각과 직접적인 요소는 아니더라도 간접적인 요인이 될 수 있지 않을까 생각한다.

Full_Code_Notebook

jhan8823 commented 4 years ago

Big 5 성격 테스트와 코로나의 연관성에 대해 재우님은 어떤 인사이트를 보여주실지 기대가 됩니다.

johnnykoo84 commented 4 years ago

좋았던 점

  1. 군더더기 없는 끝내주는 리서치 질문, 인트로 였습니다.
  2. 전체적인 분석 접근 방법과 결론은 좋습니다.

아쉬웠던 점

  1. 국가별 성향을 캐글에 있는 빅파이브 성격 테스트로 어떻게 알 수 있는지에 대한 설명이 부족해 보입니다. 아마 이 개인들의 설문지 결과가 한 국가의 성향을 나타낸다는 것이 연결점인 것 같은데 좀 더 explicit하게 저자께서 언급해 주셨으면 했습니다.
  2. 제일 처음 나오는 date vs confirmed case 에서 confirmed case가 누적인지 개별인지 명시가 필요해 보입니다.
  3. body부터 갑자기 블로그 글이 파이썬 노트북 모드로 바뀌는 것 같습니다. 설명이 너무 불친절 합니다. (code 적으론 친절할 수도 있습니다) 그래서 이후부턴 블로깅 평가가 다소 어려워졌습니다.
  4. 그럼에도 불구하고 코드와 주석을 보았습니다. test train data split에 대해선 좀 더 명확하게 왜 그렇게 하셨는지, 설명이 필요해 보입니다. 코드를 보며 추정해야 하는 부분이 아쉽습니다.

고생하셨습니다.