[Python NumPy] 절대값 함수 np.abs(x), 부호 판별 함수 np.sign(x)를 이용해서 특이값(Outlier) 찾고 다른 값으로 대체하기
Python 분석과 프로그래밍/Python 데이터 전처리 2017. 4. 17. 23:59그동안 Python NumPy의 단일 배열 unary ufuncs 에 대해서 알아보았습니다.
(http://rfriend.tistory.com/300, http://rfriend.tistory.com/301)
이번 포스팅에서는 그동안 배웠던 unary ufuncs 들을 요리조리 조합하고 응용하는 한가지 예를 들어보겠습니다.
- 절대값을 구하는 함수 np.abs(x)
- 배열 원소의 부호를 판별하는 np.sign(x) 함수
- 배열의 1개 이상의 원소가 참(True) 인지 평가하는 np.any(x) 함수
를 이용해서 특이값, 이상치(outlier)를 탐색하고, indexing해서 다른 값으로 대체하는 방법을 소개하겠습니다.
먼저 numpy, pandas, matplotlib.pyplot 모듈을 불러오고, 평균이 0, 표준편차가 1인 정규분포로 부터 난수 10,000만개를 생성해보겠습니다.
In [1]: import numpy as np ...: import pandas as pd ...: import matplotlib.pyplot as plt ...: In [2]: np.set_printoptions(precision=2) ...: ...: # setting random seed number ...: np.random.seed(10) ...: ...: # random number 10000 ~ N(0, 1) ...: mu, sigma = 0, 1 ...: x = pd.DataFrame(mu + sigma*np.random.randn(10000))
|
다음으로 dexcribe() 메서드를 사용해서 x의 기술통계량을 알아보고, 히스토그램으로 분포를 살펴보겠습니다. min과 max 값을 보면 평균 0을 중심으로 해서 -3 (-3 sigma)과 +3 (+3 sigma)을 벗어나는 관측치가 있음을 알 수 있습니다.
# checking descriptive statistics and histogram In [3]: x.describe() Out[3]: 0 count 10000.000000 mean 0.005102 std 0.989713 min -3.621639 25% -0.652208 50% 0.013111 75% 0.675040 max 3.691489 In [4]: plt.hist(x) Out[4]: (array([ 15., 139., 583., 1626., 2733., 2679., 1606., 497., 107., 15.]), array([-3.62, -2.89, -2.16, ..., 2.23, 2.96, 3.69]), <a list of 10 Patch objects>)
|
정규분포를 띠는 데이터셋의 경우 평균으로 부터 +3 sigma, -3 sigma 를 벗어나는 데이터의 경우 전체 데이터셋 중 99%가 존재하는 구간을 벗어나는 특이값, 이상값(outlier)로 간주할 수 있습니다.
이번 예제에서 x는 평균이 '0'이고 표준편차가 '1'인 정규분포를 따르므로, np.abs(x)와 any(1) 함수를 조합해서 사용하면 1만개의 관측치를 가지는 x로부터 +- 3 sigma를 벗어나는 특이값만 쏙 빼올 수 있습니다. count() 메소드로 특이값 개수도 세볼 수 있구요.
# indexing outlier rows over mu + 3sigma, less mu - 3 sigma In [5]: x[(np.abs(x) > 3).any(1)] ...: Out[5]: 0 412 -3.204401 1036 -3.317669 1558 -3.112645 2190 3.609161 3948 3.454845 4912 -3.372347 5016 -3.393109 5158 3.193371 5618 3.177053 5750 3.158873 6135 3.077068 6303 3.142285 6689 -3.621639 6760 3.027240 6986 -3.303552 7353 -3.214030 7892 3.561219 8281 3.691489 9179 3.286370 9335 3.503309
# counting the number of outliers In [6]: x[(np.abs(x) > 3).any(1)].count() Out[6]: 0 20 dtype: int64
|
이번에는 np.sign(x) 함수를 곁들여 사용하여 +- 3 sigma를 벗어나는 관측치값을 모두 +-3 으로 대체해보겠습니다. (즉, +3 sigma보다 큰 특이값은 +3으로 대체, -3 sigma 보다 작은 특이값은 -3으로 대체)
x.describe()로 요약통계량을 살펴보니 min -3, max 3으로 바뀌었지요? 히스토그램도 -3 ~ +3 까지 분포로 바뀌었구요.
In [7]: x[np.abs(x) > 3] = np.sign(x)*3 In [8]: x[(np.abs(x) >= 3).any(1)] Out[8]: 0 412 -3.0 1036 -3.0 1558 -3.0 2190 3.0 3948 3.0 4912 -3.0 5016 -3.0 5158 3.0 5618 3.0 5750 3.0 6135 3.0 6303 3.0 6689 -3.0 6760 3.0 6986 -3.0 7353 -3.0 7892 3.0 8281 3.0 9179 3.0 9335 3.0 In [9]: x.describe() Out[9]: 0 count 10000.000000 mean 0.004968 std 0.987624 min -3.000000 25% -0.652208 50% 0.013111 75% 0.675040 max 3.000000 In [10]: plt.hist(x) Out[10]: (array([ 76., 292., 755., 1554., 2269., 2299., 1648., 762., 277., 68.]), array([-3. , -2.4, -1.8, ..., 1.8, 2.4, 3. ]), <a list of 10 Patch objects>)
|
통계 분포 방법 외에 특이값 찾는 방법이 서너가지 더 있는데요, 그건 다음번에 기회 될 때 별도로 포스팅을 하겠습니다. (언제가 될지는 기약 못하겠네요. ^^;)
많은 도움 되었기를 바랍니다.