愛林

[Python/statistics] 분산분석(ANOVA) - 일원분산분석(One-way ANOVA) 본문

Data Science/DATA

[Python/statistics] 분산분석(ANOVA) - 일원분산분석(One-way ANOVA)

愛林 2022. 7. 30. 17:44
반응형

 

 

출처 : https://fullofjoy1108.tistory.com/24

 

통계 분석에는 많은 분석법이 있다.

오늘은 종속관계에 대한 분석인 분산분석(ANOVA) 에 대해 알아보자.

 

 


 

분산분석 (ANOVA ; Analysis Of Variance)

 

 

분산분석(ANOVA) 는 차이의 비율이다. (F분포 사용)

모집단이 3개 이상이면서 독립변인에 의한 종속변인의 평균치 간 차이를 검정한다.

요인별 효과를 분석하거나, 3개 이상 집단 간 모평균의 차이를 분석하는데 사용한다.

 

Ex:) 성별, 혈액형이 다른 집단 간 키,몸무게,심박수의 차이에 유의성이 있는 지 검정.

 

 

분산분석(ANOVA) 의 검정방법은 F-test 이다. (F검정 사용)

실험집단 간 분산에 대한 실험집단 내 분산의 비율을 F통계량이라고 한다.

 


 

F통계량과 분산분석(ANOVA)

 

실험 집단 간 분산에 대한 실험집단 내 분산의 비율을 F통계량이라고 한다.

 

F 통계량 = (MSA: 집단 간 분산) / (MSE : 집단 내 분산)

 

집단 내 편차와 집단 간 편차

 

관측치 = 전체 평균 + 집단 j의 처리효과 + 오차

i : 특정한 집단에 속한 관측치를 지정하는 첨자,

j : 특정한 집단을 지정하는 첨자

 

F통계량이 1근처이면, 집단 간 차이가 없다.

1보다 크면, 집단 간 차이가 있다.

 

출처  : https://bioinformaticsandme.tistory.com/198

 

A : ANOVA 검정 결과, 그룹 간의 차이가 없다.

B : ANOVA 검정 결과 , 그룹 사이 유의한 차이가 존재한다.

 

 

 

 


 

분산분석(ANOVA) 의 종류

 

 

 

1) 일원분산분석(One-way ANOVA)

 

3개 이상의 범주를 갖는 하나의 독립변수에 대한 하나의 종속변수의 평균 차이를 검정한다.

Ex:) 강남,강동,강서구 간 매장위치에 따른 만족도의 분석

 

 

2) 반복측정 분산분석(Repeated Measures ANOVA)

 

집단에 대한 반복 측정.

시간 흐름에 따른 여러 번 해당 결과를 반복적으로 측정하기 위한 분석 비법이다.

Ex: ) 실험 전, 실험 3개월 후, 실험 6개월 후에 따른 만족도의 분석

 

 

3) 이원분산분석(Two-way ANOVA)

 

두 개의 독립변수와 하나의 종속변수일 때 집단 간 차이를 분석하는 방법이다.

Ex:) 온도 1000도와 2000에 따른, 시간 1시간과 2시간 차이에 따른 맛의 변화

 

 

4) 이원반복측정 분산분석(Two-way Repeated Measures ANOVA)

 

실험군과 대조군을 나누어 동일한 실험 단위에 대해 여러 번 측정하는 경우에 사용된다.

시간 흐름에 따라 두 개의 독립변수에 따른 종속변수 차이를 반복적으로 분석하게 된다.

Ex:) 실험군과 대조군의 시험전, 시험 후 통증의 변화

 

 

 


 

일원분산분석(One-way ANOVA) 실습

 

 

일원분산분석은, 3개 이상의 범주를 갖는 하나의 실험요인에 대한 하나의 종속변수의 평균 차이를

검정하는 분석이었다.

 

stats.f_oneway() 로 scipy f통계량을 구할 수 있다.

statsmodels.stats.anova() 로 stats 모델 anova 를 구할 수 있다.

 

 

■ 증명하고자 하는 가설

4개의 각기 다른 신입사원 교육훈련 기법의 효과성을 평가하고자 한다.

새로 입사한 32명의 신입사원에게 4개 교육 기관에서 다른 학습 방법을 적용 시켰다.

한 달의 훈련기간이 끝난 후 시험을 쳤는데 그 점수는 아래와 같다. 4개 교육훈련 기법 간의 차이가 있는가 ?

만약 있다면 어떻게 다른가 ?

 

= > 4개 이상의 대응 표본(4개의 학습방법) 을 비교해야 하므로 일원분산분석을 사용한다.

 

귀무가설 : 4개의 교육훈련 기법 간의 차이가 없다.

대립가설 : 4개의 교육훈련 기법 간의 차이가 있다.

 

 

 

필요한 라이브러리들을 가져오자.

import os
import warnings
warnings.simplefilter(action = 'ignore', category = FutureWarning)

import numpy as np
import pandas as pd
import math
from scipy import stats # Scipy 패키지

import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

import os
if os.name == 'nt' : # windows OS
    font_family = "Malgun Gothic"
else : #Mac OS
    font_family = "AppleGothic"
    
# 값이 깨지는 문제 해결을 위해 파라미터값 설정
sns.set(font=font_family, rc ={"axes.unicode_minus" : False})
#데이터셋
a = [66,74,82,75,73,97,87,78]
b = [72,51,59,62,74,64,78,63]
c = [61,60,57,60,81,77,70,71]
d = [63,61,76,84,58,65,69,80]
print("a 평균 : ",np.mean(a))
print("b 평균 : ",np.mean(b))
print("c 평균 : ",np.mean(c))
print("d 평균 : ",np.mean(d))
a 평균 :  79.0
b 평균 :  65.375
c 평균 :  67.125
d 평균 :  69.5

 

box-plot 을 이용해서 이상값을 확인해주자.

 

plot_data = [a,b,c,d]
plt.boxplot(plot_data)
plt.xticks([1,2,3,4], ['a', 'b', 'c', 'd'])
plt.grid(True)
plt.show()

 

큰 이상값은 없는 것 같다.

 

이제 데이터프레임으로 변환해보자.

 

# 데이터 프레임 변환
check = ['a', 'b', 'c', 'd']
check = [check[j] for j in range(4) for i in range(8)]
data = pd.DataFrame({'점수' : a+b+c+d, '교육기관' : check})
data.head(4)

 

자료의 모집단 분포는 정규분포를 따른다. 는 정규성을 검정해보자.

 

Shapiro-wilks Normal Test 로 정규성을 검정했다.

샤피로 정규성 검정이 정규성 검정을 가장 엄격하게 한다고 한다.

 

from scipy.stats import shapiro

print('정규성을 검정')
normal_a = shapiro(a)
normal_b = shapiro(b)
normal_c = shapiro(c)
normal_d = shapiro(d)

print(normal_a)
print(normal_b)
print(normal_c)
print(normal_d)

첫 번째 값은 검정 통계량이고, 두 번째 값은  p-value 이다.

 

정규성 검정에서는 p-value 가 유의수준 0.05 보다 큰 경우 정규성을 만족한다고 본다.

다들 0.05보다 훨씬 크므로 정규성을 만족한다고 볼 수 있다.

 

 

print(stats.ks_2samp(a,b), stats.ks_2samp(a,c), stats.ks_2samp(a,d)
      , stats.ks_2samp(b,c), stats.ks_2samp(b,d), stats.ks_2samp(c,d), sep="\n")

 

ks_2samp 으로 집단 간의 정규성을 확인했다.

ks_2samp 는 Two-sample KS(Kolmogorow-Smirnow) 테스트이다.

 

 

이제 등분산성을 검정해보자.

등분산성은 모든 집단의 모분산은 동일하다는 것을 말한다.

ANOVA 를 진헹하기 위해서는, 두 집단의 모분산이 동일해야 한다.

 

## 등분산성 검정 : 모든 집단의 모분산은 동일하다
print('등분산성  검정')

# levene 등분산성 검정
from scipy.stats import levene
print(levene(a,b,c,d))

# bartlett 등분산성 검정
from scipy.stats import bartlett
print(bartlett(a,b,c,d))

 

Levene 등분산성 검정과 bartlett 등분산성 검정이 있다.

scipy.stats 에서 불러올 수 있다.

 

p-value 가 모두 0.05보다 크므로 등분산성을 만족한다.

 

 


 

F통계량을 이용한 ANOVA (stats.f_oneway)

 

이제 F통계량을 이용하여 가설을 검정해보자.

 

f_statistic, pval = stats.f_oneway(a,b,c,d)

print('F={0:.1f}, p={1:.3f}'.format(f_statistic, pval))

if pval < 0.05 :
    print("귀무가설 기각, 4개의 교육훈련기법 간 차이가 있다.")
else : 
    print("귀무가설 채택, 4개의 교육훈련기법 간 차이가 없다.")

stats.f_oneway 에 a,b,c,d 데이터들을 넣어주었다.

t_statistic 은  t 검정 통계량,  pval 은 p-value 를 보여준다.

 

p-value 가 0.05보다 작으면, 기각역이므로 귀무가설은 기각되고

4개의 교육기법은 차이가 있다 라는 대립가설이 채택된다.

 

결과는 기각역.

p-value 가 0.029 < 0.05 이므로 대립가설이 채택되었다.

4개의 교육훈련기법 간에는 차이가 있다.

 


Statsmodel 을 이용한 ANOVA (statsmodel.anova)

 

 

이번에는 f통계량이 아닌, Statsmodel 을 이용하여 가설을  검정해보자.

 

model = ols('종속변수 ~ 독립변수' , data).fit() : model 

sm.stats.anova_lm(model,type=2) 를 사용하면 Type2 아노바 데이터프레임이다.

 

 

from statsmodels.formula.api import ols
from statsmodels.stats.anova import anova_lm
model_1A = ols('점수 ~ C(교육기관)', data).fit()
anova_lm_result = anova_lm(model_1A)
anova_lm_result

 

교육 기관 간 평균 편차는 295.0833, 

Residual 은 잔차이다. 편차 이는 C 집단 내 편차를 이야기한다.

 

왜 이렇게 나왔느냐,

우리가 아까 만든 data 데이터프레임은

 

대충 이렇다.

data.shape 을 찍어보면 32, 2가 나온다,

여기서 SSA(집단간 제곱합) 는 K-1 이므로 4-1 = 3

SSE(집단내 제곱합) 는 N-K 이므로 32 - 4 = 28

SST(총 제곱합) 는 N-1 = 32 - 1 = 31

MSA(집단 간 분산) / MSE(집단 내 분산) = 295.083333 / 84.741071 = 3.482176 이다. (F통계량)

PR(>F) 는 p-value 이다. 아까 구한 0.029 와 비슷한 값인 0.028897 이 나왔다.

 

 

pval = anova_lm_result['PR(>F)'][0]

if pval < 0.05 :
    print("귀무가설 기각, 4개의 교육훈련 기법간에 차이가 있다.")
else :
    print("귀무가설 채택, 4개의 교육훈련 기법간에 차이가 없다.")

 

귀무가설 기각, 4개의 교육훈련 기법간에 차이가 있다.

 


 

사후분석

 

 

F검정을 통해서 집단 간 차이가 유의하더라도, 어떤 집단 간의 차이인지 명확하게 확인하기 위해서

사후검정(Post-hoc Analysis) 을 진행한다. (유의미하지 않을 시에는 사후 분석이 불필요하다.)

 

사후 검정 방식 중에 대표적인 것은 Tukey, Bonferonni, Scheff 이다.

사후 검정 민감도는 Scheffe > Tukey > Duncan 순이다.

 

 

 

위에서 구한 결과로 사후 분석을 진행해보자,

ANOVA 검증 결과 유의미하다는 결론을 얻었을 때, 구체적으로 어떤 수준에서 평균 차이가 나는 지를

검증해야 할 필요가 있다.

 

Tukey 의 HSD 를 사용해서 사후 분석을 진행해볼 것이다.

 

from statsmodels.stats.multicomp import pairwise_tukeyhsd

HSD = pairwise_tukeyhsd(data['점수'], data['교육기관'], alpha = 0.05)
HSD.summary()

 

Tukey 의 HSD 를 이용하여 사후 분석을 실시한 결과,

a조건과 b조건에서 유의미한 평균 차이가 있었다. (True) (p<0.05)

 

 

 


 

이원분산분석 (Two-way ANOVA)

 

이원분산분석은 종속변수가 1개, 독립변수가 2개이면서 독립변수 각각이 요인으로서 요인 내에

수준을 가지고 있을 때, 각 집단 평균의 유의미한 차이가 있는 지 유무를 확인한다.

 

 

■ 증명하고자 하는 가설

귀무가설 : 강도에 대한 각 판유리와 온도 간에는 상호작용 효과가 없다.

대립가설 : 강도에 대한 각 판유리와 온도 간에는 상호작용 효과가 있다.

 

 

dat=pd.DataFrame({'판유리':['유리1', '유리1', '유리1', '유리2', '유리2', '유리2', '유리3', '유리3',
                            '유리3', '유리1', '유리1', '유리1', '유리2', '유리2', '유리2', '유리3', 
                            '유리3', '유리3', '유리1', '유리1', '유리1', '유리2', '유리2', '유리2', 
                            '유리3', '유리3', '유리3'], 
                  '온도':[100, 100, 100, 100, 100, 100, 100, 100, 100, 125, 125, 125, 125, 125,
                          125, 125, 125, 125, 150, 150, 150, 150, 150, 150, 150, 150, 150], 
                  '강도':[580, 568, 570, 550, 530, 579, 546, 575, 599, 1090, 1087, 1085, 1070,
                          1035, 1000, 1045, 1053, 1066, 1392, 1380, 1386, 1328, 1312, 1299, 867, 
                          904, 889]
                 })

 

dat 데이터를 만들어주었다.

 

 

우리의 종속변수는 '강도' 이고,

독립변수는 '판유리' 와 '온도' 이다.

 

 

판유리와 온도 요인으로 구분한 각 집단별 표본 수는 모두 3으로 동일했다.

 

dat.groupby(['판유리', '온도']).agg(len)

 

from statsmodels.formula.api import ols
from statsmodels.stats.anova import anova_lm

model = ols('강도 ~ C(판유리) * C(온도)', dat).fit()
anova_result = pd.DataFrame(anova_lm(model))

위에서 했던 거 똑같이 해준다.

 

 

판유리 : F(2,18) = 206.370554 . p-value < 0.05 로 유의미하다. 즉 판유리의 수준에 따라 평균에 차이가 난다.

온도 : F(2,18) = 2695.259499 , p-value < 0.05 로 유의미하다. 즉, 온도에 따라 평균에 차이가 난다.

판유리와 온도 F(4,18)  = 198.725707, p-value < 0.05 로 유의미하다. 상호작용 효과를 발견했다.

 

 

이제 사후분석을 진행해보자. Tukey 의 HSD 사용.

 

 

dat['온도'] = dat['온도'].astype('str')
dat['combi'] = dat.판유리 + "/" + dat.온도
print(pairwise_tukeyhsd(dat['강도'], dat['combi']))
    Multiple Comparison of Means - Tukey HSD, FWER=0.05    
===========================================================
 group1  group2  meandiff p-adj    lower     upper   reject
-----------------------------------------------------------
유리1/100 유리1/125  514.6667    0.0  459.9706  569.3627   True
유리1/100 유리1/150  813.3333    0.0  758.6373  868.0294   True
유리1/100 유리2/100  -19.6667 0.9307  -74.3627   35.0294  False
유리1/100 유리2/125  462.3333    0.0  407.6373  517.0294   True
유리1/100 유리2/150  740.3333    0.0  685.6373  795.0294   True
유리1/100 유리3/100    0.6667    1.0  -54.0294   55.3627  False
유리1/100 유리3/125     482.0    0.0  427.3039  536.6961   True
유리1/100 유리3/150     314.0    0.0  259.3039  368.6961   True
유리1/125 유리1/150  298.6667    0.0  243.9706  353.3627   True
유리1/125 유리2/100 -534.3333    0.0 -589.0294 -479.6373   True
유리1/125 유리2/125  -52.3333  0.067 -107.0294    2.3627  False
유리1/125 유리2/150  225.6667    0.0  170.9706  280.3627   True
유리1/125 유리3/100    -514.0    0.0 -568.6961 -459.3039   True
유리1/125 유리3/125  -32.6667 0.5066  -87.3627   22.0294  False
유리1/125 유리3/150 -200.6667    0.0 -255.3627 -145.9706   True
유리1/150 유리2/100    -833.0    0.0 -887.6961 -778.3039   True
유리1/150 유리2/125    -351.0    0.0 -405.6961 -296.3039   True
유리1/150 유리2/150     -73.0 0.0046 -127.6961  -18.3039   True
유리1/150 유리3/100 -812.6667    0.0 -867.3627 -757.9706   True
유리1/150 유리3/125 -331.3333    0.0 -386.0294 -276.6373   True
유리1/150 유리3/150 -499.3333    0.0 -554.0294 -444.6373   True
유리2/100 유리2/125     482.0    0.0  427.3039  536.6961   True
유리2/100 유리2/150     760.0    0.0  705.3039  814.6961   True
유리2/100 유리3/100   20.3333  0.918  -34.3627   75.0294  False
유리2/100 유리3/125  501.6667    0.0  446.9706  556.3627   True
유리2/100 유리3/150  333.6667    0.0  278.9706  388.3627   True
유리2/125 유리2/150     278.0    0.0  223.3039  332.6961   True
유리2/125 유리3/100 -461.6667    0.0 -516.3627 -406.9706   True
유리2/125 유리3/125   19.6667 0.9307  -35.0294   74.3627  False
유리2/125 유리3/150 -148.3333    0.0 -203.0294  -93.6373   True
유리2/150 유리3/100 -739.6667    0.0 -794.3627 -684.9706   True
유리2/150 유리3/125 -258.3333    0.0 -313.0294 -203.6373   True
유리2/150 유리3/150 -426.3333    0.0 -481.0294 -371.6373   True
유리3/100 유리3/125  481.3333    0.0  426.6373  536.0294   True
유리3/100 유리3/150  313.3333    0.0  258.6373  368.0294   True
유리3/125 유리3/150    -168.0    0.0 -222.6961 -113.3039   True
-----------------------------------------------------------

 

해석 : 유리 1도가 100도일 때와 125도일 때의 유의미한 차이가 있었고,

100도와 150도일 때에도 유의미한 차이가 있었다.

그렇지만 유리 1과 유리 2가 100도일 때는 유의미한 차이가 없었다.

.

.

.

.

 

 

 

 


 

ANOVA 는 더 공부해야겠다 ..

뭔가 복잡한 기분.. 어려웡 ㅠ

 

 

 

 ※ 해당 자료는 모두 공공 빅데이터 청년 인턴 교육자료들을 참고합니다.

Comments