Q-Q plot을 이용한 normality test
2 분 소요
현재 데이터 분포가 가우시안분포를 따르는가?
- 다양한 확률분포가 있는데, 그중에서, 수치들을 ‘가우시안 분포’를 따른다고 가정하고 모델링할 때가 많습니다. 그런데, 이건 가정이잖아요. 편하기는 한데, 정말 해당 데이터가 “가우시안 분포”를 정말 따르는지, 그건 어떻게 알 수 있을까요?
Q-Q plot(Quantile Quantile plot)
- Q-Q plot은 유사공대생의 입장에서 말을 하자면, normal dist를 따를 때, quantile value와 현재 데이터 분포 상에서의 quantile 값을 scattering해주는 것과 비슷합니다.
- 약간, 헷갈리는 부분이 있는데, 그냥, plotting 결과가 선형 상에서 삐뚤게 나타나면, normal dist를 따르지 않고, 따르면 normal dist라고만 우선을 생각하셔도 아주 문제는 없습니다. 일단 훑어보고 나중에 다시 봅시다 하하핳
- 사실, 그래서, q-q plot을 그리지 않고, 간단히 box-plot만 그려도 대략 비슷하게 알 수있습니다.
draw q-q plot
scipy.stats.probplot
를 사용해 q-q plot을 그려줍니다. 어떤 값을 받아서 그려주는 것이 아니고, 바로 그려집니다. 그냥 plt.show()
를 하면 됩니다.
- normal, exponential, log1p 를 각각 그려줬는데, 당연히, normal dist가 적당히 길쭉하게 잘 나옵니다 하하핫
from scipy.stats import probplot
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(0)
n = 100
x1 = np.random.normal(0, 1, n)
x2 = np.random.exponential(2, n)
x3 = np.log1p(x2)
f, axes = plt.subplots(2, 3, figsize=(12, 6))
axes[0][0].boxplot(x1)
probplot(x1, plot=axes[1][0]) #scipy.stats.probplot
axes[0][1].boxplot(x2)
probplot(x2, plot=axes[1][1]) #scipy.stats.probplot
axes[0][2].boxplot(x3)
probplot(x3, plot=axes[1][2]) #scipy.stats.probplot
plt.axis("equal")
plt.savefig('../../assets/images/markdown_img/180619_1347_qqplot.svg')
plt.show()
- 아래 그림에서 보는 것처럼, q-q plot에서 알 수 있는 것을 boxplot에서도 대략 알 수 있습니다.
central limit theorem
- 학부 2학년때(와 10년 넘었다) central limit theorem을 배운 기억이 나네요. 유사공대생인 저는 이제 와서, 이걸 다시 깨달았습니다….후….ㅠㅠㅠ
- 아무튼, centrali limit theorem이란 다음을 말합니다.
- 평균과 분산이 같은 여러 개의 독립변수(X1…Xn)가 있을 때
- 이 독립변수의 합으로 만든 새로운 독립변수를 Xsum이라고 하고,
- Xsum의 분포를 norm(0, 1)을 따르도록 표준화시켜주었을 때(이건 생략도 가능합니다)
- Xsum은 n이 커질수록 가우시안분포를 따른다는 것.
- 예전에는 바보처럼 sample size가 커질수록 좋다는 식으로 이해했네요…ㅠㅠㅠ흑 유사공대생
- 아무튼, 이 말대로면, 어떤 분포든 n이 커지면 노멀 분포를 따른다는거죠?? 해봅시다.
"""
central limit theorem
- 평균과 분산이 같은 여러 개의 독립변수(X1...Xn)가 있을 때
- 이 독립변수의 합인 Xsum이라고 하고
- Xsum의 분포를 norm(0, 1)을 따르도록 표준화시켜주었을 때,
- Xsum은 n이 커질수록 가우시안분포를 따른다는 것.
"""
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.stats import probplot
n = 200
# n의 값이 다른 여러 가지 확률 분포를 가진 X_sum 독립변수를 만들어 줌
xss = np.array([
np.array([np.random.exponential(2, n) for i in range(0, 1)]).sum(axis=0),
np.array([np.random.exponential(2, n) for i in range(0, 5)]+
[np.random.uniform(2, 4, n)]).sum(axis=0),
np.array([np.random.exponential(2, n) for i in range(0, 20)]+
[np.random.uniform(2, 4, n)]).sum(axis=0)
])
# apply_along_axis는 늘 헷갈리는데....저는 사실 그냥 map을 쓰는게 더 편하기는 합니다 하하핫
xss = np.apply_along_axis(lambda xs: (xs-xs.mean())/xs.std() , 1, xss)
f, axes = plt.subplots(xss.shape[0], 2, figsize=(12, 8))
for i in range(0, xss.shape[0]):
sns.distplot(xss[i], ax=axes[i][0])
probplot(xss[i], plot=axes[i][1])
plt.savefig('../../assets/images/markdown_img/180619_1342_central_limit_prove.svg')
plt.show()
- 독립분포 여러 개를 합칠 수록 가우시안분포를 따르게 되네요 하하핫
사실 평균과 분산이 같을 필요는 없어요.
- 어차피, 평균과 분산을 표준화시켜주면 되거든요. 다음처럼 평균과 분산이 다른 것들을 합쳐도 n이 커지면 알아서 qqplot도, distplot도 모두 잘 나옵니다.
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.stats import probplot
n = 200
# n의 값이 다른 여러 가지 확률 분포를 가진 X_sum 독립변수를 만들어 줌
xss = [
np.random.exponential(np.random.randint(2, 100), n) for i in range(0, 20)
]
xss = list(map(lambda xs: (xs-xs.mean())/xs.std(), xss ))
xss = np.array(xss).sum(axis=0)
#xss = np.apply_along_axis(lambda xs: (xs-xs.mean())/xs.std() , 1, xss).sum(axis=0)
f, axes = plt.subplots(1, 2, figsize=(12, 4))
sns.distplot(xss, ax=axes[0])
probplot(xss, plot=axes[1])
plt.savefig("../../assets/images/markdown_img/180619_1413_different_mean_var.svg")
plt.show()
wrap-up
- 이제 데이터 분석 전에 해주어야 하는 것이 두 가지로 늘었습니다. -
- skewness 확인
- normality test by qqplot(or boxplot)
reference
댓글남기기