[Python] 이상치, 특이값이 들어있는 데이터의 표준화 (Scaling data with outliers)
Python 분석과 프로그래밍/Python 데이터 전처리 2016. 12. 15. 23:40지난번 포스팅에서는 zscore(), StandardScaler() 등을 사용해서 척도(scale)가 다른 변수들을 X ~ N(0, 1) 의 표준정규분포로 변환시키는 표준화에 대해서 알아보았습니다.
그런데 표준정규분포로의 표준화 변환 시에는 "이상치, 특이값 (outlier)이 없어야 한다"는 가정사항이 있습니다. 표준정규분포로 변환하는 공식이 z = (x - mean) / std 이며, 평균(mean)은 이상치, 특이값에 엄청 민감하기 때문입니다.
그럼, 데이터에 "이상치"가 포함되어 있다면 어떻게 해야할까요?
첫번째 방법은 "이상치, 특이값을 찾아서 제거"한 후 표준정규분포로 표준화 변환을 해서 분석, 모델링을 진행하는 방법입니다. "이상치, 특이값을 찾아서 제거"하는 노~력이 필요합니다. 물론, 회귀분석과 같은 parametric modeling 에서는 이상치 제거 후 모델링이 적합한 방법입니다.
두번째 방법은 "이상치, 특이값에 덜 민감한" 중앙값(median)과 IQR(Inter-Quartile Range)을 이용해서 척도를 표준화하는 방법입니다. K-NN 같은 non-parametric modeling 은 두번째 방법도 써볼만 합니다.
이번 포스팅의 주제가 바로 두번째 방법에 대한 것입니다.
이번 포스팅의 주인공은 RobustScaler() 이지만, 왜 필요하고 무엇이 표준정규분포 표준화와 다른지를 이해하기 쉽도록 하기 위해서, 이상치(outlier)를 포함하고 있는 동일한 예제 데이터에 대해서 Python 의 sklearn.preprocessing의
(1) StandardScaler() method를 이용한 표준정규분포로의 표준화 ((x-mean)/std )와
(2) RobustScaler() method를 이용한 표준화 ( (x-median)/IQR )를
비교하면서 설명을 해보겠습니다.
먼저, 필요한 모듈을 불러오고, 이상치, 특이값을 포함하고 있는 예제 데이터를 만들어보겠습니다.
# importing modules In [1]: import numpy as np In [2]: from sklearn.preprocessing import StandardScaler, RobustScaler In [3]: import matplotlib.pyplot as plt In [4]: import pandas as pd
# setting the number of digits of precision for floating point output In [5]: np.set_printoptions(precision=2)
# setting random seed number In [6]: np.random.seed(10)
# making 100 random x ~ N(10, 2) In [7]: mu, sigma = 10, 2 In [8]: x = mu + sigma*np.random.randn(100) In [9]: x Out[9]: array([ 12.66, 11.43, 6.91, 9.98, 11.24, 8.56, 10.53, 10.22,
In [10]: plt.hist(x) Out[10]: (array([ 6., 3., 8., 15., 22., 20., 11., 9., 3., 3.]), array([ 5.74, 6.66, 7.58, 8.5 , 9.42, 10.34, 11.26, 12.18, 13.1 , 14.02, 14.94]), <a list of 10 Patch objects>)
# checking mean, std In [11]: np.mean(x) # 10.15 (about 10) Out[11]: 10.158833325873747 In [12]: np.std(x) # 1.93 (about 2) Out[12]: 1.9340789542274115
In [13]: x[98:100] = 100 In [14]: x Out[14]: array([ 12.66, 11.43, 6.91, 9.98, 11.24, 8.56, 10.53,
In [15]: np.mean(x) # 11.98 Out[15]: 11.981383595820532 In [16]: np.std(x) # 12.71 Out[16]: 12.714552555982538
# plotting histogram to see the change of distribution, especially by outliers In [17]: plt.hist(x, bins=np.arange(0, 102, 2)) Out[17]: (array([ 0., 0., 1., 10., 36., 34., 14., 3., 0., 0., 0., <a list of 50 Patch objects>)
|
(1) 이상치가 포함된 데이터의 표준정규분포로의 표준화 : |
# reshape from 1d arrays to ndarray In [18]: x = x.reshape(-1, 1)
# numpy.ndarray In [19]: x[0:10] Out[19]: array([[ 12.66],
# By StandardScaler() In [20]: x_StandardScaler = StandardScaler().fit_transform(x) In [21]: x_StandardScaler Out[21]: ..... (중간 생략) ..... [ -4.38e-02],
In [22]: np.mean(x_StandardScaler) # mean = 0 Out[22]: 5.3290705182007512e-17 In [23]: np.std(x_StandardScaler) # std = 1 Out[23]: 1.0
In [24]: plt.hist(x_StandardScaler) Out[24]: (array([ 98., 0., 0., 0., 0., 0., 0., 0., 0., 2.]), array([-0.49, 0.25, 0.99, 1.73, 2.47, 3.22, 3.96, 4.7 , 5.44, 6.18, 6.92]), <a list of 10 Patch objects>)
# zoom in In [25]: x_StandardScaler_zoonin = x_StandardScaler[x_StandardScaler < 5] In [26]: plt.hist(x_StandardScaler_zoonin, bins=np.arange(-3.0, 3.0, 0.2)) Out[26]: (array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 5., 26., 50., 14., 3., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]), array([ -3.00e+00, -2.80e+00, -2.60e+00, -2.40e+00, -2.20e+00, -2.00e+00, -1.80e+00, -1.60e+00, -1.40e+00, -1.20e+00, -1.00e+00, -8.00e-01, -6.00e-01, -4.00e-01, -2.00e-01, 2.66e-15, 2.00e-01, 4.00e-01, 6.00e-01, 8.00e-01, 1.00e+00, 1.20e+00, 1.40e+00, 1.60e+00, 1.80e+00, 2.00e+00, 2.20e+00, 2.40e+00, 2.60e+00, 2.80e+00]), <a list of 29 Patch objects>)
|
위의 '표준정규분포로의 표준화' 예시의 제일 마지막에 이상치(outlier)를 무시하고 표준화 이후 값 범위 (-3 ~ 3) 사이로 그린 히스토그램을 아래의 RobustScaler()로 표준화를 한 값과 비교해보시기 바랍니다.
StandardScaler() 에 의한 표준화가 이상치에 영향을 더 심하게 받아서 이상치가 아닌 값들이 조밀하게, 촘촘하게 서로 붙어있음을 알 수 있습니다.
(2) 이상치가 포함된 데이터의 중앙값과 IQR 를 이용한 표준화 : sklearn.preprocessing.RobustScaler() |
In [27]: np.median(x) # 10.2 Out[27]: 10.207697741550213 In [28]: Q1 = np.percentile(x, 25, axis=0) In [29]: Q1 # 9.04 Out[29]: array([ 9.04]) In [30]: Q3 = np.percentile(x, 75, axis=0) # 5.700 In [31]: Q3 # 11.4 Out[31]: array([ 11.42]) In [32]: IQR = Q3 - Q1 In [33]: IQR # 2.37 Out[33]: array([ 2.37]) In [34]: x_RobustScaler = RobustScaler().fit_transform(x)
# list up 10 elememts from backward In [35]: x_RobustScaler[-10:] Out[35]: array([[ 8.46e-01],
# RobustScaler() removes the median and scales the data according to IQR(Inerquartile Range) In [36]: np.median(x_RobustScaler)
Out[36]: 0.0 In [37]: np.mean(x_RobustScaler) Out[37]: 0.74729363822182793 In [38]: np.std(x_RobustScaler) Out[38]: 5.3569262082386482
# checking the distribution by histogram after RobustScaler In [39]: plt.hist(x_RobustScaler) Out[39]: (array([ 98., 0., 0., 0., 0., 0., 0., 0., 0., 2.]),
# zoom in In [40]: x_RobustScaler_zoonin = x_RobustScaler[x_RobustScaler < 5] In [41]: plt.hist(x_RobustScaler_zoonin, bins=np.arange(-3, 3, 0.2)) Out[41]: (array([ 0., 0., 0., 0., 0., 1., 3., 1., 3., 1., 4.,
|
아래의 두 개의 히스토그램은 이상치, 특이값(outlier)이 포함되어 있는 데이터를 표준화하는 경우에 (1) 평균과 표준편차를 이용한 표준정규분포 표준화 결과 (outlier 미포함한 범위의 zoom in)와, (2) 중앙값과 IQR(Interquartile Range)를 이용한 이상치에 견고한 표준화 (outlier 미포함한 범위의 zoom in) 결과의 분포를 나타내고 있습니다.
왼쪽의 StandardScaler()에 의한 표준화보다 오른쪽의 RobustScaler()에 의한 표준화가 동일한 값을 더 넓게 분포시키고 있음을 알 수 있습니다. 즉, 목표변수 y값을 분류나 예측하는데 있어 산포가 더 크기 때문에 설명변수 x변수로서 더 유용할 수 있다고 추정할 수 있습니다.
(1) StandardScaler() zoon in |
(2) RobustScaler() zoon in |
|
|
다음번 포스팅에서는 최소.최대 [0~1] 범위 변환에 대해서 소개하도록 하겠습니다.
많은 도움 되었기를 바랍니다.