AI 공부/머신러닝

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

AI Sonny 2022. 9. 7. 14:38
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