SciPy를 사용한 기초적인 검정

SciPy 파이썬 패키지는 다음과 같은 다양한 검정 명령을 제공한다.

  • 이항 검정 (Binomial test)
  • 카이 제곱 검정 (Chi-square test)
  • 단일 표본 z-검정 (One-sample z-test)
  • 단일 표본 t-검정 (One-sample t-test)
  • 독립 표본 t-검정 (Independent-two-sample t-test)
  • 대응 표본 t-검정 (Paired-two-sample t-test)
  • 분산 검정 (Chi squared variance test)
  • 등분산 검정 (Equal-variance test)
  • 정규성 검정 (Normality test)

이항 검정 (Binomial test)

이항 검정은 이항 분포를 이용하여 Bernoulli 분포 모수 $\theta$에 대한 가설을 조사하는 검정 방법이다. SciPy stats 서브패키지의 binom_test 명령을 사용한다. 디폴트 귀무 가설은 $\theta = 0.5$이다.

데이터 갯수 $N=10$, 실제 모수 $\theta_0=0.5$인 경우 대해 이항 검정 명령을 실시해 보자.


In [55]:
N = 10
theta_0 = 0.5
np.random.seed(0)
x = sp.stats.bernoulli(theta_0).rvs(N)
n = np.count_nonzero(x)
n


Out[55]:
7

In [56]:
sp.stats.binom_test(n, N)


Out[56]:
0.34374999999999989

유의 확률(p-value)이 34%로 높으므로 귀무 가설을 기각할 수 없다. 따라서 $\theta=0.5$이다.

데이터 갯수 $N=100$, 실제 모수 $\theta_0=0.5$인 경우 대해 이항 검정 명령을 실시해 보자.


In [57]:
N = 100
theta_0 = 0.5
np.random.seed(0)
x = sp.stats.bernoulli(theta_0).rvs(N)
n = np.count_nonzero(x)
n


Out[57]:
49

In [58]:
sp.stats.binom_test(n, N)


Out[58]:
0.92041076261282062

유의 확률(p-value)이 92%로 높으므로 귀무 가설을 기각할 수 없다. 따라서 $\theta=0.5$이다.

데이터 갯수 $N=100$, 실제 모수 $\theta_0=0.35$인 경우 대해 이항 검정 명령을 실시해 보자.


In [59]:
N = 100
theta_0 = 0.35
np.random.seed(0)
x = sp.stats.bernoulli(theta_0).rvs(N)
n = np.count_nonzero(x)
n


Out[59]:
31

In [60]:
sp.stats.binom_test(n, N)


Out[60]:
0.00018314322488235352

유의 확률(p-value)이 0.018%로 낮으므로 귀무 가설을 기각할 수 있다. 따라서 $\theta \neq 0.5$이다.

카이 제곱 검정 (Chi-square test)

카이 제곱 검정은 goodness of fit 검정이라고도 부른다. 카테고리 분포의 모수 $\theta=(\theta_1, \ldots, \theta_K)$에 대한 가설을 조사하는 검정 방법이다. SciPy stats 서브패키지의 chisquare 명령을 사용한다. 디폴트 귀무 가설은 $\theta = \left(\frac{1}{K}, \ldots, \frac{1}{K} \right)$이다.

데이터 갯수 $N=10$, 실제 모수 $\theta_0=(0.25, 0.25, 0.25, 0.25)$인 경우 대해 카이 제곱 검정 명령을 실시해 보자.


In [61]:
N = 10
K = 4
theta_0 = np.ones(K)/K
np.random.seed(0)
x = np.random.choice(K, N, p=theta_0)
n = np.bincount(x, minlength=K)
n


Out[61]:
array([0, 3, 5, 2])

In [62]:
sp.stats.chisquare(n)


Out[62]:
Power_divergenceResult(statistic=5.1999999999999993, pvalue=0.157724450396663)

유의 확률(p-value)이 17.8%로 높으므로 귀무 가설을 기각할 수 없다. 따라서 $\theta_0=(0.25, 0.25, 0.25, 0.25)$이다.

데이터 갯수 $N=100$, 실제 모수 $\theta_0=(0.35, 0.30, 0.20, 0.15)$인 경우 대해 카이 제곱 검정 명령을 실시해 보자.


In [63]:
N = 100
K = 4
theta_0 = np.array([0.35, 0.30, 0.20, 0.15])
np.random.seed(0)
x = np.random.choice(K, N, p=theta_0)
n = np.bincount(x, minlength=K)
n


Out[63]:
array([37, 32, 20, 11])

In [64]:
sp.stats.chisquare(n)


Out[64]:
Power_divergenceResult(statistic=16.559999999999999, pvalue=0.00087034719789121269)

유의 확률(p-value)이 0.087%이므로 귀무 가설을 기각할 수 있다. 따라서 $\theta \neq (0.25, 0.25, 0.25, 0.25))$이다.

단일 표본 z-검정 (One-sample z-test)

단일 표본 z-검정은 분산 $\sigma^2$의 값을 정확히 알고 있는 정규 분포의 표본에 대해 기댓값을 조사하는 검정방법이다. 단일 표본 z-검정의 경우에는 SciPy에 별도의 함수가 준비되어 있지 않으므로 norm 명령의 cdf 메서드를 사용하여 직접 구현해야 한다.

데이터 갯수 $N=10$, 실제 모수 $\mu_0=0$인 경우 대해 단일 표본 z-검정 명령을 실시해 보자.


In [49]:
N = 10
mu_0 = 0
np.random.seed(0)
x = sp.stats.norm(mu_0).rvs(N)
x


Out[49]:
array([ 1.76405235,  0.40015721,  0.97873798,  2.2408932 ,  1.86755799,
       -0.97727788,  0.95008842, -0.15135721, -0.10321885,  0.4105985 ])

In [47]:
def ztest_1samp(x, sigma2=1, mu=0):
    z = (x.mean() - mu)/ np.sqrt(sigma2/len(x))
    return z, 2 * sp.stats.norm().sf(np.abs(z))

In [48]:
ztest_1samp(x)


Out[48]:
(2.3338341854824276, 0.019604406021683538)

유의 확률(p-value)이 1.96%이므로 만약 유의 수준이 5% 이상 이라면 귀무 가설을 기각할 수 있다. 따라서 $\mu \neq 0$이다. 이 경우는 검정 결과가 오류인 예라고 볼 수 있다. 검정 결과가 오류로 나온 이유는 데이터 수가 10개로 부족하기 때문이다.

오류의 유형 중에서 이러한 오류는 귀무 가설이 진실임에도 불구하고 거짓으로 나온 경우로 유형 1 오류(Type 1 Error)라고 한다.

데이터 갯수 $N=100$, 실제 모수 $\mu_0=0$인 경우 대해 단일 표본 z-검정 명령을 실시해 보자.


In [73]:
N = 100
mu_0 = 0
np.random.seed(0)
x = sp.stats.norm(mu_0).rvs(N)

In [74]:
ztest_1samp(x)


Out[74]:
(0.59808015534484993, 0.54978645086241684)

유의 확률(p-value)이 54.98%이므로 귀무 가설을 기각할 수 없다. 따라서 $\mu = 0$이다.

단일 표본 t-검정 (One-sample t-test)

단일 표본 t-검정은 정규 분포의 표본에 대해 기댓값을 조사하는 검정방법이다. SciPy의 stats 서브 패키지의 ttest_1samp 명령을 사용한다. ttest_1samp 명령의 경우에는 디폴트 모수가 없으므로 popmean 인수를 사용하여 직접 지정해야 한다.

데이터 갯수 $N=10$, 실제 모수 $\mu_0=0$인 경우 대해 단일 표본 z-검정 명령을 실시해 보자.


In [89]:
N = 10
mu_0 = 0
np.random.seed(0)
x = sp.stats.norm(mu_0).rvs(N)
sns.distplot(x, kde=False, fit=sp.stats.norm)
plt.show()



In [83]:
sp.stats.ttest_1samp(x, popmean=0)


Out[83]:
Ttest_1sampResult(statistic=2.2894396723896699, pvalue=0.047818464908570578)

유의 확률(p-value)이 4.78%이므로 만약 유의 수준이 5% 이상 이라면 귀무 가설을 기각할 수 있다. 따라서 $\mu \neq 0$이다. 이 경우는 검정 결과가 오류인 예라고 볼 수 있다. 검정 결과가 오류로 나온 이유는 데이터 수가 10개로 부족하기 때문이다.

데이터 갯수 $N=100$, 실제 모수 $\mu_0=0$인 경우 대해 단일 표본 z-검정 명령을 실시해 보자.


In [90]:
N = 100
mu_0 = 0
np.random.seed(0)
x = sp.stats.norm(mu_0).rvs(N)
sns.distplot(x, kde=False, fit=sp.stats.norm)
plt.show()



In [85]:
sp.stats.ttest_1samp(x, popmean=0)


Out[85]:
Ttest_1sampResult(statistic=0.59042834028516977, pvalue=0.55624891586946745)

유의 확률(p-value)이 55.62%이므로 귀무 가설을 기각할 수 없다. 따라서 $\mu = 0$이다.

독립 표본 t-검정 (Independent-two-sample t-test)

독립 표본 t-검정(Independent-two-sample t-test)은 간단하게 two sample t-검정이라고도 한다. 두 개의 독립적인 정규 분포에서 나온 두 개의 데이터 셋을 사용하여 두 정규 분포의 기댓값이 동일한지를 검사한다. SciPy stats 서브패키지의 ttest_ind 명령을 사용한다. 독립 표본 t-검정은 두 정규 분포의 분산값이 같은 경우와 같지 않은 경우에 사용하는 검정 통계량이 다르기 때문에 equal_var 인수를 사용하여 이를 지정해 주어야 한다.

두 정규 분포의 기댓값이 $\mu_1 = 0$, $\mu_2 = 1$으로 다르고 분산은 $\sigma_1 = \sigma_2 = 1$ 으로 같으며 샘플의 수가 $N_1=N_2=10$인 경우를 실행해 보자


In [99]:
N_1 = 10; mu_1 = 0; sigma_1 = 1
N_2 = 10; mu_2 = 0.5; sigma_2 = 1
np.random.seed(0)
x1 = sp.stats.norm(mu_1, sigma_1).rvs(N_1)
x2 = sp.stats.norm(mu_2, sigma_2).rvs(N_2)
sns.distplot(x1, kde=False, fit=sp.stats.norm)
sns.distplot(x2, kde=False, fit=sp.stats.norm)
plt.show()



In [100]:
sp.stats.ttest_ind(x1, x2, equal_var=True)


Out[100]:
Ttest_indResult(statistic=-0.41399685269886549, pvalue=0.68376768941164268)

유의 확률(p-value)이 68.4%이므로 귀무 가설을 기각할 수 없다. 따라서 $\mu_1 \neq \mu_2$이다. 이 경우는 검정 결과가 오류인 예라고 볼 수 있다.

오류의 유형 중에서 이러한 오류는 귀무 가설이 거짓임에도 불구하고 진실로 나온 경우로 유형 2 오류(Type 2 Error)라고 한다.

데이터 수가 증가하면 이러한 오류가 발생할 가능성이 줄어든다.


In [105]:
N_1 = 50; mu_1 = 0; sigma_1 = 1
N_2 = 100; mu_2 = 0.5; sigma_2 = 1
np.random.seed(0)
x1 = sp.stats.norm(mu_1, sigma_1).rvs(N_1)
x2 = sp.stats.norm(mu_2, sigma_2).rvs(N_2)
sns.distplot(x1, kde=False, fit=sp.stats.norm)
sns.distplot(x2, kde=False, fit=sp.stats.norm)
plt.show()



In [106]:
sp.stats.ttest_ind(x1, x2, equal_var=True)


Out[106]:
Ttest_indResult(statistic=-2.6826951236616963, pvalue=0.0081339709157226582)

데이터의 갯수를 50개와 100개로 증가시킨 경우에 유의 확률은 0.8%로 감소하였다. 따라서 두 확률 분포의 기댓값이 일치한다는 귀무 가설은 기각할 수 있다.

대응 표본 t-검정 (Paired-two-sample t-test)

대응 표본 t-검정은 독립 표본 t-검정을 두 집단의 샘플이 1대1 대응하는 경우에 대해 수정한 것이다. 즉, 독립 표본 t-검정과 마찬가지로 두 정규 분포의 기댓값이 같은지 확인하기 위한 검정이다.

예를 들어 어떤 반의 학생들이 특강을 수강하기 전과 수강한 이후에 각각 시험을 본 시험 점수의 경우에는 같은 학생의 두 점수는 대응할 수 있다. 이 대응 정보를 알고 있다면 보통의 독립 표본 t-검정에서 발생할 수 있는 샘플간의 차이의 영향을 없앨 수 있기 때문에 특강 수강의 영향을 보다 정확하게 추정할 수 있다.

$\mu_1 = 0$, $\mu_2 = 0.5$로 평균이 달라진 경우에 대해 대응 표본 t-검정을 실시해 보자. 데이터 갯수 $N$은 5 이다.


In [119]:
N = 5
mu_1 = 0
mu_2 = 0.5
np.random.seed(1)
x1 = sp.stats.norm(mu_1).rvs(N)
x2 = x1 + sp.stats.norm(mu_2, 0.1).rvs(N)
sns.distplot(x1, kde=False, fit=sp.stats.norm)
sns.distplot(x2, kde=False, fit=sp.stats.norm)
plt.show()



In [120]:
sp.stats.ttest_rel(x1, x2)


Out[120]:
Ttest_relResult(statistic=-7.1723380661732756, pvalue=0.0020008849290622677)

5 개의 데이터만으로도 두 평균이 다르다는 것을 유의 확률(p-value) 0.2%의 정확도로 알아내었음을 확인할 수 있다.

카이 제곱 분산 검정 (Chi-Square Test for the Variance)

지금까지는 정규 분포의 기댓값을 비교하는 검정을 살펴보았다. 이제는 정규 분포의 분산에 대해 살펴보자.

카이 제곱 분산 검정(Chi-Square Test for the Variance)은 정규 분포의 샘플 분산 값은 정규화 하면 카이 제곱 분포를 따른다는 점을 이용하는 검정 방법이다.

그러나 SciPy는 카이 제곱 분산 검정에 대한 명령이 없으므로 chi2 클래스를 사용하여 직접 구현해야 한다.


In [134]:
def chi2var_test(x, sigma2=1):
    v = x.var(ddof=1)
    t = (len(x) - 1)*v/sigma2
    return t, sp.stats.chi2(df=len(x)-1).sf(np.abs(t))

In [153]:
N = 10
mu_0 = 0
sigma_0 = 1.1
np.random.seed(0)
x = sp.stats.norm(mu_0, sigma_0).rvs(N)
sns.distplot(x, kde=False, fit=sp.stats.norm)
plt.show()
x.std()


Out[153]:
1.0637871321863899

In [154]:
chi2var_test(x)


Out[154]:
(11.316430626053437, 0.25464123584764531)

등분산 검정 (Equal-variance test)

등분산 검정은 두 정규 분포로부터 생성된 두 개의 데이터 집합으로부터 두 정규 분포의 분산 모수가 같은지 확인하기 위한 검정이다. 가장 기본적인 방법은 F분포를 사용하는 것이지만 실무에서는 이보다 더 성능이 좋은 bartlett, fligner, levene 방법을 주로 사용한다. SciPy의 stats 서브패키지는 이를 위한 bartlett, fligner, levene 명령을 제공한다.


In [188]:
N1 = 100
N2 = 100
sigma_1 = 1
sigma_2 = 1.2
np.random.seed(0)
x1 = sp.stats.norm(0, sigma_1).rvs(N1)
x2 = sp.stats.norm(0, sigma_2).rvs(N2)
sns.distplot(x1, kde=False, fit=sp.stats.norm)
sns.distplot(x2, kde=False, fit=sp.stats.norm)
plt.show()
x1.std(), x2.std()


Out[188]:
(1.0078822447165796, 1.2416003969261071)

In [189]:
sp.stats.bartlett(x1, x2)


Out[189]:
BartlettResult(statistic=4.2534738372322662, pvalue=0.039170128783651344)

In [190]:
sp.stats.fligner(x1, x2)


Out[190]:
FlignerResult(statistic=7.2248419904094572, pvalue=0.0071901501067483673)

In [191]:
sp.stats.levene(x1, x2)


Out[191]:
LeveneResult(statistic=7.6807089476794372, pvalue=0.0061135154970207925)

정규성 검정

회귀 분석 등에서는 확률 분포가 가우시안 정규 분포를 따르는지 아닌지를 확인하는 것이 중요하다. 이러한 검정을 정규성 검정(normality test)이라고 한다. 정규성 분포 그 중요도 만큼 여러가지 검정 방법들이 개발되어 있으며 Scipy 패키지 이외에도 statsmodels 패키지에도 다양한 정규성 검정 명령어를 제공한다.

statsmodels에서 제공하는 정규성 검정 명령어

SciPy 에서 제공하는 정규성 검정 명령어

이 중에서 Kolmogorov-Smirnov 검정은 사실 정규 분포에 국한되지 않고 두 샘플이 같은 분포를 따르는지 확인할 수 있는 방법이다.


In [203]:
np.random.seed(0)
N1 = 50
N2 = 100
x1 = sp.stats.norm(0, 1).rvs(N1)
x2 = sp.stats.norm(0.5, 1.5).rvs(N2)
sns.distplot(x1)
sns.distplot(x2)
plt.show()



In [204]:
sp.stats.ks_2samp(x1, x2)


Out[204]:
Ks_2sampResult(statistic=0.23000000000000004, pvalue=0.049516112814422863)

유의 확률이 4.95%로 만약 유의 수준이 5%라면 두 분포는 서로 다른 분포라고 볼 수 있다.