YOGYUI

R caret::preProcess - 수치형 데이터 정규화, 표준화 본문

Software/R

R caret::preProcess - 수치형 데이터 정규화, 표준화

요겨 2021. 6. 17. 11:39
반응형

 

caret 패키지의 preProcess 함수를 활용하면 수치형 데이터 정규화 (normalization) 및 표준화 (standardization)을 쉽게 수행할 수 있으며, 특정 데이터셋에 적용된 min, max, average 등 파라미터를 다른 데이터셋에 적용하는 것도 가능하다 

(ex: 머신러닝 훈련용 데이터셋에 정규화 적용 후, 계산시 사용된 값을 테스트용 데이터셋에 그대로 적용)

 

iris 데이터를 두 세트로 나눈뒤 실습해보도록 한다

set.seed(210617)

library(caret)
df <- iris
idx <- caret::createDataPartition(df$Species, p = 0.6)
df_train <- df[idx$Resample1, ]
df_test <- df[-idx$Resample1, ]

df_train: 레코드 수 90, df_test: 레코드 수 60

table(df_train$Species)
>
    setosa versicolor  virginica 
        30         30         30 

table(df_test$Species)
>
    setosa versicolor  virginica 
        20         20         20 

 

1. 정규화 (Normalization)

수치형 데이터의 범위를 [0, 1]로 조정한다

\(x'={{x - x_{min}} \over {x_{max}-x_{min}}}\)

데이터 속성들의 범위가 천차만별일 경우 regression 모델의 예측 정확도를 향상시킬 수 있다

preProcess의 method = "range" 인자로 구현할 수 있다

norm <- caret::preProcess(df_train, method = 'range')
norm
> Created from 90 samples and 5 variables

Pre-processing:
  - ignored (1)
  - re-scaling to [0, 1] (4)

Species 속성은 'factor'형이므로 계산 시 제외되었음을 알 수 있다

norm$ranges
>
     Sepal.Length Sepal.Width Petal.Length Petal.Width
[1,]          4.3         2.2          1.0         0.1
[2,]          7.7         4.4          6.9         2.5

preProcess 객체에서 계산에 사용된 min, max 값을 알 수 있다 (1행 = min값, 2행 = max값)

df_train의 summary를 통해 확인한 min, max값과 동일한 것을 알 수 있다

summary(df_train)
>
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width         Species  
 Min.   :4.300   Min.   :2.200   Min.   :1.000   Min.   :0.10   setosa    :30  
 1st Qu.:5.200   1st Qu.:2.800   1st Qu.:1.525   1st Qu.:0.30   versicolor:30  
 Median :5.750   Median :3.000   Median :4.400   Median :1.35   virginica :30  
 Mean   :5.847   Mean   :3.061   Mean   :3.740   Mean   :1.22                  
 3rd Qu.:6.375   3rd Qu.:3.375   3rd Qu.:5.100   3rd Qu.:1.80                  
 Max.   :7.700   Max.   :4.400   Max.   :6.900   Max.   :2.50 

 

preProcess 객체를 predict 함수를 사용해 데이터셋에 적용할 수 있다

df_train_norm <- predict(norm, df_train)
df_test_norm <- predict(norm, df_test)
summary(df_train_norm)
>
  Sepal.Length     Sepal.Width      Petal.Length      Petal.Width            Species  
 Min.   :0.0000   Min.   :0.0000   Min.   :0.00000   Min.   :0.00000   setosa    :30  
 1st Qu.:0.2647   1st Qu.:0.2727   1st Qu.:0.08898   1st Qu.:0.08333   versicolor:30  
 Median :0.4265   Median :0.3636   Median :0.57627   Median :0.52083   virginica :30  
 Mean   :0.4549   Mean   :0.3914   Mean   :0.46441   Mean   :0.46667                  
 3rd Qu.:0.6103   3rd Qu.:0.5341   3rd Qu.:0.69492   3rd Qu.:0.70833                  
 Max.   :1.0000   Max.   :1.0000   Max.   :1.00000   Max.   :1.00000      
 
summary(df_test_norm)
>
  Sepal.Length      Sepal.Width        Petal.Length     Petal.Width            Species  
 Min.   :0.02941   Min.   :-0.09091   Min.   :0.0339   Min.   :0.00000   setosa    :20  
 1st Qu.:0.23529   1st Qu.: 0.27273   1st Qu.:0.1017   1st Qu.:0.08333   versicolor:20  
 Median :0.44118   Median : 0.36364   Median :0.5508   Median :0.50000   virginica :20  
 Mean   :0.45245   Mean   : 0.38712   Mean   :0.4720   Mean   :0.44514                  
 3rd Qu.:0.61765   3rd Qu.: 0.50000   3rd Qu.:0.7331   3rd Qu.:0.70833                  
 Max.   :1.05882   Max.   : 0.77273   Max.   :0.9153   Max.   :1.00000  

 

머신러닝 구현 시, 훈련용 데이터에 적용된 데이터 전처리 과정을 테스트 데이터에도 그대로 적용해야 올바른 결과를 얻을 수 있는데, caret::preProcess 객체와 predict 함수를 통해 손쉽게 구현할 수 있다

 

직접 함수로 구현하면 다음과 같다

func_norm <- function(x) {
    (x - min(x)) / (max(x) - min(x))
}

df_train_norm2 <- sapply(df_train[, 1:4], func_norm)
summary(df_train_norm2)
>
  Sepal.Length     Sepal.Width      Petal.Length      Petal.Width     
 Min.   :0.0000   Min.   :0.0000   Min.   :0.00000   Min.   :0.00000  
 1st Qu.:0.2647   1st Qu.:0.2727   1st Qu.:0.08898   1st Qu.:0.08333  
 Median :0.4265   Median :0.3636   Median :0.57627   Median :0.52083  
 Mean   :0.4549   Mean   :0.3914   Mean   :0.46441   Mean   :0.46667  
 3rd Qu.:0.6103   3rd Qu.:0.5341   3rd Qu.:0.69492   3rd Qu.:0.70833  
 Max.   :1.0000   Max.   :1.0000   Max.   :1.00000   Max.   :1.00000 
sum(df_train_norm[, 1:4] - df_train_norm2)
> [1] 0

두 결과물 간 값의 차이는 없는 것을 알 수 있다

 

단, 직접 함수로 구현하면 테스트 데이터에 적용 시 각 속성별 min, max값을 따로 저장해뒀다가 사용해야 하니 꽤 번거롭다

 

2. 표준화 (Standardization)

평균이 0, 표준편차가 1 (= 분산이 1)인 표준정규분포로 값을 변환

\(z={{x-\bar{x}}\over{\sigma_{x}}}\)

\(\bar{x}\)는 평균, \(\sigma_{x}\)는 표준편차를 말한다

 

preProcess의 method="center" 일 경우 데이터에서 평균을 빼주게 되며 (\(x-\bar{x}\)),  method="scale"일 경우 데이터의 표준편차로 나누어 준다 (\(/{\sigma_{x}}\))

두 방법을 다음과 같이 하나로 묶어 표준화 객체를 생성할 수 있다

pstd <- caret::preProcess(df_train, method = c("center", "scale"))
pstd
> Created from 90 samples and 5 variables

Pre-processing:
  - centered (4)
  - ignored (1)
  - scaled (4)

각 속성별 계산에 사용된 평균과 표준편차를 다음과 같이 확인할 수 있다

pstd$mean
>
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
    5.846667     3.061111     3.740000     1.220000 

pstd$std
>
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
   0.8296744    0.4523831    1.7787636    0.7756404 

데이터에 적용하는 방법을 predict로 동일하다

df_train_std <- predict(pstd, df_train)
df_test_std <- predict(pstd, df_test)
summary(df_train_std)
>
  Sepal.Length      Sepal.Width       Petal.Length      Petal.Width            Species  
 Min.   :-1.8642   Min.   :-1.9035   Min.   :-1.5404   Min.   :-1.4440   setosa    :30  
 1st Qu.:-0.7794   1st Qu.:-0.5772   1st Qu.:-1.2452   1st Qu.:-1.1861   versicolor:30  
 Median :-0.1165   Median :-0.1351   Median : 0.3710   Median : 0.1676   virginica :30  
 Mean   : 0.0000   Mean   : 0.0000   Mean   : 0.0000   Mean   : 0.0000                  
 3rd Qu.: 0.6368   3rd Qu.: 0.6939   3rd Qu.: 0.7646   3rd Qu.: 0.7478                  
 Max.   : 2.2338   Max.   : 2.9596   Max.   : 1.7765   Max.   : 1.6502                  

summary(df_test_std)
>
  Sepal.Length       Sepal.Width        Petal.Length      Petal.Width             Species  
 Min.   :-1.74366   Min.   :-2.34560   Min.   :-1.4280   Min.   :-1.44397   setosa    :20  
 1st Qu.:-0.89995   1st Qu.:-0.57719   1st Qu.:-1.2031   1st Qu.:-1.18612   versicolor:20  
 Median :-0.05625   Median :-0.13509   Median : 0.2867   Median : 0.10314   virginica :20  
 Mean   :-0.01004   Mean   :-0.02088   Mean   : 0.0253   Mean   :-0.06661                  
 3rd Qu.: 0.66693   3rd Qu.: 0.52807   3rd Qu.: 0.8911   3rd Qu.: 0.74777                  
 Max.   : 2.47487   Max.   : 1.85438   Max.   : 1.4954   Max.   : 1.65025  

 

직접 함수로 구현하면 다음과 같다

func_std <- function(x) {
    (x - mean(x)) / (sd(x))
}

df_train_std2 <- sapply(df_train[, 1:4], func_std)
sum(df_train_std[, 1:4] - df_train_std2)
> [1] 0

두 결과물은 역시 동일하다

 

이 외에도 method 인자에 쓸 수 있는 다양한 옵션이 존재한다

"pca": 주성분분석(Principal Component Analysis)

"knnImpute": K-Nearest Neighbor 알고리즘 결측치 대체

"bagImpute": Bagged-Tree 알고리즘 결측치 대체

"medianImpute": 중앙값으로 결측치를 대체

"nzv": near zero variance 변수 제거 (caret::nearZeroVariance 참고)


데이터 전처리는 (일반적으로) 다음 과정으로 진행하면 제일 무난하다

NA 처리 - 표준화 - 아웃라이어 감지 및 처리 - 정규화

NA는 imputation(대체) 혹은 (수가 많지 않을 경우) 레코드 제거

아웃라이어 감지는 BoxPlot, IQR 등 시각화를 사용하거나, Z-score 판별, 스튜던트화 잔차 이용, Grubss 검정, Dixon 검정, Rosner 검정 등 다양한 방식이 있다 (따로 글을 작성해야 할듯)

 

caret 패키지만 제대로 쓸 줄 알면 데이터 전처리의 거의 전과정을 패키지 하나로 해결할 수 있다

 

[참고]

서민구, 『R을 이용한 데이터 처리 & 분석 실무』, 길벗(2014), p532-536.

https://lumiamitie.github.io/r/preProcess/

https://huidea.tistory.com/39

 

반응형