[ 변수 처리] 결측치 대체 알고리즘 MissForest Imputation 연습

2019. 12. 11. 22:59분석 Python/Data Preprocessing

728x90

 

MissForest로 결측치 대체를 하려고하는 것이 목적이다.
그래서 일단 임의의 데이터를 만들고 진행한다.

from missingpy import MissForest
import numpy as np
import pandas as pd
from sklearn.preprocessing import LabelEncoder

tr1 = list("ABCD")
tr2 = ['pooh', 'rabbit', 'piglet', 'Christopher']
va1 = list("ABCDE")
va2 = ['pooh', 'rabbit', 'piglet', 'Christopher', "bird", "coco"]

tr1_ = np.random.choice(tr1, 1000 , p=[0.5, 0.1, 0.1, 0.3])
va1_ = np.random.choice(va1, 500 , p=[0.4, 0.1, 0.1, 0.3,0.1])
tr2_ = np.random.choice(tr2, 1000 , p=[0.5, 0.1, 0.1, 0.3])
va2_ = np.random.choice(va2, 500 , p=[0.5, 0.1, 0.09, 0.25,0.05,0.01])

tr_n = np.random.normal(size = (1000 , 10))
va_n = np.random.normal(size = (500 , 10))

col = [ "Cols{}".format(i) for i in np.arange(10)]
Tr = pd.DataFrame(tr_n, columns= col)
Va = pd.DataFrame(va_n , columns= col)

Tr["Col_Fac1"] = tr1_
Tr["Col_Fac2"] = tr2_
Va["Col_Fac1"] = va1_
Va["Col_Fac2"] = va2_

Tr.head()

이제 이러한 데이터에서 결측을 만들어보자.
가정은 MCAR로 임의로 만들었다.  위에서 만들수도 있지만, 그냥 여기서 MaskVector를 만들어서 진행했다.

No , ori = Tr.shape
p_miss = 0.2
p_miss_vec = p_miss * np.ones((No,1)) 
Missing = np.zeros((No,ori))
ori_Missing = np.zeros((No,ori))
for idx , st in enumerate(np.arange(ori)) :
    A = np.random.uniform(0., 1., size = [No,])
    A1 = np.expand_dims(A , axis = 1)
    B_ = A > p_miss_vec[idx]
    ori_Missing[:,idx] = 1.*B_
    B = A1 > p_miss_vec
    
No , ori = Va.shape
p_miss = 0.2
p_miss_vec = p_miss * np.ones((No,1)) 
Missing = np.zeros((No,ori))
va_Missing = np.zeros((No,ori))
for idx , st in enumerate(np.arange(ori)) :
    A = np.random.uniform(0., 1., size = [No,])
    A1 = np.expand_dims(A , axis = 1)
    B_ = A > p_miss_vec[idx]
    va_Missing[:,idx] = 1.*B_
    B = A1 > p_miss_vec
MaskVector = 1-ori_Missing
VaMaskVector = 1-va_Missing
Tr[MaskVector == 1 ]= np.nan
Va[VaMaskVector==1] = np.nan

 그 다음에 간단한 전처리 진행을 했다.

fac_var = ["Col_Fac1" , "Col_Fac2"]
change_nan = {}
for col in fac_var :
    unis = list(set(Va[col].unique()).difference(set(Tr[col].unique())))
    temp = {}
    for uni in unis :
        temp[uni] = np.nan
    change_nan[col] = temp
    
Va2 = Va.replace(change_nan)

그 다음에 label encoder를 적용하기 위해 결측치를 다른 값으로 대체하고 해당 encoder값을 저장했다. 

le_info = {}
for col in fac_var :
    Tr[col] = Tr[col].fillna("NaN")
    Va2[col] = Va2[col].fillna("NaN")
    le = LabelEncoder()
    x = Tr[col].unique()
    le_info[col] = le.fit(x)
    le.fit(x)
    Tr[col] = le.transform(Tr[col])
    Va2[col] = le.transform(Va2[col])
    
le_info[col].classes_
    

결측을 NaN으로 만들었기 때문에 다시 MaskVector를 사용해서 결측으로 만들었다. 아니면 해당 encoding 값을 바꾸는 것도 다른 하나의 방법일 것이다.

Tr[MaskVector == 1 ]= np.nan
Va2[VaMaskVector==1] = np.nan

Va2.head()

이제 데이터는 다 구성했으니, MissForest 결측치 대체 알고리즘을 돌려보자!
큰 특징은 cat_var로 범주형 변수의 index 정보를 줘야 한다!

UseCols = Tr.columns.tolist()
cat_var = [idx for idx , col in enumerate(UseCols) if col in fac_var]
MISSForest_algo = MissForest(verbose = 0, n_jobs  = -1 , max_depth=5)
MISSForest_algo.fit(X =Tr  , cat_vars= cat_var)
Tr_imputed = MISSForest_algo.transform(Tr)
Tr_imputed = pd.DataFrame(Tr_imputed, columns= UseCols
Tr_imputed[fac_var] = Tr_imputed[fac_var].astype(int)
Va_imputed = MISSForest_algo.transform(Va2)
Va_imputed = pd.DataFrame(Va_imputed, columns= UseCols)
Va_imputed[fac_var] = Va_imputed[fac_var].astype(int)
## 다시 변형
for col in fac_var :
    Tr_imputed[col] = le_info[col].inverse_transform(Tr_imputed[col])
    Va_imputed[col] = le_info[col].inverse_transform(Va_imputed[col])
Tr_imputed.isna().sum()

도움이 되셨다면, 광고 한번만 눌러주세요.  블로그 운영에 큰 힘이 됩니다 ^^

728x90