본문 바로가기
AI 공부/머신러닝

(머신러닝) 결측치 및 스케일링

by AI Sonny 2022. 9. 7.
728x90

seaborn에 있는 타이타닉 데이터를 가져와서 결측치와 스케일링을 적용시켜 보겠다.

 

import pandas as pd
import numpy as np
import seaborn as sns
from sklearn.preprocessing import OneHotEncoder
from sklearn.model_selection import train_test_split

df = sns.load_dataset("titanic")

SEED = 42
df_train, df_test = train_test_split(df, random_state=SEED, test_size=0.2)
df_train

 

먼저 필요한 라이브러리를 불러와주고, 데이터를 train과 test에 담아준다.

 

df_train = df_train.reset_index(drop=True)
df_test = df_test.reset_index(drop=True)
cols = ["pclass","sex","age","sibsp","parch","fare","embarked"]
x_train = df_train[cols]
x_test = df_test[cols]
x_train.shape, x_test.shape

=> ((712, 7), (179, 7))

 

이 후 인덱스를 reset한 후 drop해주고, 컬럼을 지정해준다.

 


결측치(Missing Value)

 

결측치를 다루기 전에 데이터의 정보를 알아보는 것이 중요하다.

 

x_train.info()

=>
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 712 entries, 0 to 711
Data columns (total 7 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   pclass    712 non-null    int64  
 1   sex       712 non-null    object 
 2   age       572 non-null    float64
 3   sibsp     712 non-null    int64  
 4   parch     712 non-null    int64  
 5   fare      712 non-null    float64
 6   embarked  710 non-null    object 
dtypes: float64(2), int64(3), object(2)
memory usage: 39.1+ KB

 

결측치가 age, embarked에 존재하는 것을 알 수 있다.

 


통계치를 이용하여 결측값 채우기

 

pandas 메소드를 이용한 방법

 

age_median = x_train["age"].median() 
x_train["age"].fillna(age_median)

=>
0      45.5
1      23.0
2      32.0
3      26.0
4       6.0
       ... 
707    21.0
708    28.0
709    41.0
710    14.0
711    21.0
Name: age, Length: 712, dtype: float64

 

위 코드에서 test에 채우려고 하는 새로운 변수에 담는다. (test는 모르는 값이기 때문에) 

 

sklearn 모듈을 이용한 방법

- 주요 메소드

  • fit : 통계치를 추출
  • fit_transformn : 통계치 추출과 함께 데이터 변환 (numpy 배열로 반환)
  • transformn : fit 또는 fit_transform 통해서 추출된 통계치를 반영해서 데이터 변환

예시

 

from sklearn.impute import SimpleImputer

imputer = SimpleImputer(strategy="mean")
imputer.fit_transform(x_train[["age"]]) # 2차원형태로 넣으라해서 [[]]을 사용

=>
array([[45.5       ],
       [23.        ],
       [32.        ],
       [26.        ],
       [ 6.        ],
       [24.        ],
       [45.        ],
       [29.        ],
       [29.49884615],
       [29.49884615],
       .
       .

 

결측치에 평균값을 넣었다.

 

머신러닝 모델을 이용하여 결측치 채우기

  • 결측치가 아닌 다른 변수들을 이용하여 결측치를 추정
  • 주변에 결측치가 아닌 값들을 이용해서 결측치를 예측하여 채우는 방식
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer

 

결측치 확인

 

x_train.tail()

=>
pclass	sex	age	sibsp	parch	fare	embarked
3	female	21.0	0	0	7.6500	S
1	male	NaN	0	0	31.0000	S
3	male	41.0	2	0	14.1083	S
1	female	14.0	1	2	120.0000 S
1	male	21.0	0	1	77.2875	S

 

age에 결측치가 존재하는 것을 확인

 

from sklearn.linear_model import LinearRegression
imputer = IterativeImputer(estimator =  LinearRegression(),random_state=SEED) # IterativeImputer에서 다양한 옵션이 있다. (estimator는 머신러닝 옵션!)
tmp = imputer.fit_transform(x_train[["pclass","age","sibsp","parch","fare"]])
tmp[-5:]

=>
array([[  3.        ,  21.        ,   0.        ,   0.        ,
          7.65      ],
       [  1.        ,  40.63970136,   0.        ,   0.        ,
         31.        ],
       [  3.        ,  41.        ,   2.        ,   0.        ,
         14.1083    ],
       [  1.        ,  14.        ,   1.        ,   2.        ,
        120.        ],
       [  1.        ,  21.        ,   0.        ,   1.        ,
         77.2875    ]])

 

결측치를 LinearRegression을 이용하여 결측치를 채웠다.


결측치 채우기

display(x_train.info())
display(x_test.info())

=>
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 712 entries, 0 to 711
Data columns (total 7 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   pclass    712 non-null    int64  
 1   sex       712 non-null    object 
 2   age       572 non-null    float64
 3   sibsp     712 non-null    int64  
 4   parch     712 non-null    int64  
 5   fare      712 non-null    float64
 6   embarked  710 non-null    object 
dtypes: float64(2), int64(3), object(2)
memory usage: 39.1+ KB
None
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 179 entries, 0 to 178
Data columns (total 7 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   pclass    179 non-null    int64  
 1   sex       179 non-null    object 
 2   age       142 non-null    float64
 3   sibsp     179 non-null    int64  
 4   parch     179 non-null    int64  
 5   fare      179 non-null    float64
 6   embarked  179 non-null    object 
dtypes: float64(2), int64(3), object(2)
memory usage: 9.9+ KB
None

 

x_train에는 결측치가 age, embarked에 있고, x_test에는 결측치가 age가 있다.

 

age_median = x_train["age"].median()
x_train["age"] = x_train["age"].fillna(age_median)
x_test["age"] = x_test["age"].fillna(age_median)
embarked_mode = x_train["embarked"].mode()[0]
x_train["embarked"] = x_train["embarked"].fillna(embarked_mode)

 

 

각 결측치가 중앙값과 최빈값으로 채워진 것을 볼 수 있다.

 

display(x_train.info())
display(x_test.info())

=>
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 712 entries, 0 to 711
Data columns (total 7 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   pclass    712 non-null    int64  
 1   sex       712 non-null    object 
 2   age       712 non-null    float64
 3   sibsp     712 non-null    int64  
 4   parch     712 non-null    int64  
 5   fare      712 non-null    float64
 6   embarked  712 non-null    object 
dtypes: float64(2), int64(3), object(2)
memory usage: 39.1+ KB
None
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 179 entries, 0 to 178
Data columns (total 7 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   pclass    179 non-null    int64  
 1   sex       179 non-null    object 
 2   age       179 non-null    float64
 3   sibsp     179 non-null    int64  
 4   parch     179 non-null    int64  
 5   fare      179 non-null    float64
 6   embarked  179 non-null    object 
dtypes: float64(2), int64(3), object(2)
memory usage: 9.9+ KB
None

범주형 인코딩

  • label encoding
    • 범주형 변수의 N개 종류의 값들을 0에서 n-1 값으로 숫자를 부여하는 인코딩이다.
    • 원핫인코딩과 다르게 범주의 개수와 상관없이 피처가 1개만 나온다.
    • 대소관계가 들어가 학습에 방해가 될 수 있다.
  • 우연히 높게 나올 수 있어서 추천하는 방법은 아니다.

 

예시

 

from sklearn.preprocessing import LabelEncoder

enc = LabelEncoder()
enc.fit_transform(x_train["embarked"]) # 1차원 넣어야함!

=>
array([2, 2, 2, 2, 2, 0, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2,
       2, 2, 2, 0, 0, 2, 2, 0, 2, 0, 2, 1, 1, 2, 2, 2, 0, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 2, 2, 2, 2, 0, 1, 2, 2, 0, 2, 0,
       2, 2, 2, 2, 2, 2, 2, 1, 0, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 0, 2, 1, 2, 2,
       2, 2, 1, 0, 0, 2, 2, 1, 0, 0, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, 2, 2,
       2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 0, 0, 2,
       2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 1, 2, 0, 2, 0, 2, 0, 2, 2, 2, 2,
       2, 2, 2, 1, 2, 2, 2, 1, 2, 0, 1, 2, 2, 2, 2, 0, 0, 1, 2, 2, 2, 2,
       2, 0, 0, 0, 2, 2, 0, 2, 2, 2, 0, 2, 1, 0, 0, 0, 2, 2, 2, 2, 2, 2,
       2, 2, 1, 0, 2, 0, 1, 0, 2, 2, 2, 0, 2, 2, 0, 2, 1, 2, 2, 2, 0, 0,
       2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 1, 1,
       2, 2, 2, 2, 2, 2, 2, 0, 0, 1, 2, 2, 2, 2, 0, 2, 0, 1, 1, 0, 1, 2,
       0, 2, 0, 2, 1, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 0, 2, 0, 0, 1, 2, 2,
       2, 2, 2, 0, 1, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       0, 0, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 0, 2, 2, 1,
       2, 2, 1, 2, 2, 2, 2, 2, 2, 1, 0, 2, 2, 2, 2, 0, 2, 1, 0, 2, 2, 2,
       2, 1, 0, 2, 2, 2, 2, 1, 1, 2, 0, 2, 1, 2, 2, 2, 2, 2, 2, 0, 2, 2,
       2, 1, 2, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       1, 2, 0, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 0, 0, 2,
       1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0,
       1, 2, 2, 0, 1, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 0, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2,
       0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2,
       0, 2, 2, 2, 0, 2, 2, 0, 2, 0, 2, 2, 2, 2, 0, 2, 0, 2, 2, 2, 1, 2,
       2, 2, 2, 2, 0, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2,
       2, 2, 1, 1, 2, 1, 1, 1, 2, 2, 2, 0, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 0, 0, 2, 2, 0, 2, 1, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 0, 2,
       2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2,
       0, 2, 2, 2, 2, 0, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 0, 2, 2, 2, 2, 0,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, 2, 1, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2,
       2, 0, 2, 2, 2, 2, 2, 2])

 

- Ordinal encoding

  • 순서형 변수에 매우 적합한 인코딩 방식
  • class 같이 대소관계가 있는 경우 적합하다.
  • 범주형 데이터가 많으면 일일이 바꿔야해서 힘들 수 있다.

 

예시

 

ordinal_dict = {
    1 : 0,
    2 : 1,
    3 : 2
}
x_train["pclass"].map(ordinal_dict)

=>
0      0
1      1
2      2
3      2
4      2
      ..
707    2
708    0
709    2
710    0
711    0
Name: pclass, Length: 712, dtype: int64

 


Feature Scaling

 

from sklearn.linear_model import LogisticRegression 

model = LogisticRegression(random_state=SEED)
model.fit(x_train,y_train)

=>
/usr/local/lib/python3.7/dist-packages/sklearn/linear_model/_logistic.py:818: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  extra_warning_msg=_LOGISTIC_SOLVER_CONVERGENCE_MSG,
LogisticRegression(random_state=42)

 

오차가 최고점에 수렴하지 못해서 오류 발생! 그래서 스케일링을 해야한다.

 

728x90

'AI 공부 > 머신러닝' 카테고리의 다른 글

(머신러닝) 머신러닝 모델  (2) 2022.09.13
(머신러닝) sklearn  (0) 2022.09.11
(머신러닝) 교차검증과 과적합  (0) 2022.09.07
(머신러닝) 성능측정  (0) 2022.09.06
인공지능과 머신러닝의 개념  (2) 2022.09.05

댓글