본문 바로가기
귀찮겠지만 ANOVA의 Post Hoc (사후분석)을 해 보자

이번에 다루는 이야기는 분산분석(ANOVA) 후에 통계량이 유의(Significant)하다라는 검정 결과를 얻었을 때, 그렇다면 집단 중에 어느 집단이 도대체 다른 것인가를 찾아내는 이야기입니다. ANOVA의 Alternative Hypothesis는 최소한 1개의 집단은 평균이 다르다이니까 어느 것이 다른지는 모르는 상황인 것이죠. 여기에서 중요한 사실은 그러면 t Testing을  $_n C_2$번 하면 찾아내는 것 아닌가. 하겠지만, 분산분석에서  $\alpha : 1-(1-\alpha)^{_n C_2}$ 로 유의수준이 커지니까 Type I Error가 늘어난다는 것을 ANOVA 분석할 때 이미 봤으니 그것이 최선은 아니라는 사실이 마음에 걸리지 않겠습니까. - Fisher는 이런 날것의 방법으로 사후 비교 검정을 하긴 했습니다. - 사실 그렇게 Pair를 맞춰서 Testing 하면서 α를 5%로 유지•보정하는 방법이 있습니다. 

이런 분석들을 사후분석이라고 해서 Post Hoc이라고 퉁쳐서 부르는데, Fisher's LSD(피셔의 LSD), Tukey' HSD 투키의 HSD), Bonferroni correction (봉페로니교정), Duncan (던칸의 방법), Scheffe (셰페의 방법), Games Howell (게임즈 하웰) 등이 있는데 말이죠. 아니, 뭐가 또 이렇게 많지요? 하는 마음속의 깊은 킹받음이 나오기 직전인데. 그냥 그 나름대로의 사정들이 있으니 이번에도 그냥 넘어가 주고 이해해 주는 것이 좋겠습니다.라고 했지만 지금 말한 것보다 훨씬 많은 Post Hoc방법이 즐비합니다. 이 정도라도 아는 것이 다행이라고나 할까요. 아니 내 친구 이름들도 가물한데, 전부다 사람 이름으로 이름을 지어 놓으면 어쩌자는 겁니까. 흙. 이것을 전부 기억하는 것은 절대로 무리입니다. 참고로 Post Hoc은 라틴어로 "after this"의 뜻입니다.

그래도 전술한 대표격인 여러 가지 Post Hoc 방법이 있는데 도대체 무슨 차이가 있는 것인지 알아야 하겠습니다. 모두 등분산을 가정합니다. 

⓵ Fisher's LSD : 이거야 말로 집단을 짝지어서 t Test를 아무런 보정 없이 걍 하는 방법입니다. 최근에는 완전 비추

⓶ Tuckey' HSD : 비교 집단간 표본크기가 동일한 경우에만 사용하고요, t분포를 활용합니다. 보통 이공계열에서는 엄격한 제한된 조건의 실험을 하는 경우가 많아서 사용되는 경우가 많습니다. 이공대생이라면 아는 것이 신상에 좋습니다.  

⓷ Bonferroni : t 검정을 짝지어서 검정한 후에 유의수준α을 5%로 만들고 - 이 보정된 유의수준을 FWER이라고 부릅니다. - p value를 짝지은 수만큼 곱해서 보정하는 것이니까, Fisher's LSD와 같지만 조금 사정이 낫습니다. 사실상 가장 일반적이고 현실적인 방법입니다. 

⓸ Duncan : Duncan은 작은 차이에도 차이가 난다고 결과를 내 놓습니다. 그러니까 집단 간 차이가 꼭 드러나야 하는 경우라면 Duncan 방법을 사용하는 것이 좋겠습니다. Duncan방법은 사회과학 등에서 설문 등을 할 때 많이 이용합니다. 사회과학도들은 알고 있는 것이 신상에 좋겠습니다. 

⓹ Sheffe 방법 : 큰 차이가 나야 차이가 난다는 결과를 내 놓습니다. 가장 보수적인 방법이라고 생각하면 되겠습니다. 웬만한 차이로는 차이를 구분하지 못하고요, F분포를 활용해서 검정합니다.  

여기까지만 알아봐요. 더 알아보다가 머리가 터지면 안되거든요. 

여기까지가 일반적인 ANOVA의 Post Hoc 방법론이고, 등분산이 아닌 경우에는 Welch 검정을 한다고 했었죠. 이런 경우에 Post Hoc은 

⓺ Games Howell 

이 방법을 사용해서 Post Hoc을 하는데요, Welch의 방법으로 t분포의 자유도를 바꿔서 검정합니다. 

Post Hoc 검정 방법들은 뭐 이정도로 살펴보는 것이 정신건강에 좋을 것 같습니다. 그냥 Tukey, Bonferroni와 Games Howel 정도만 대표적으로 알고 있으면 되지 않을까 합니다. 

참고로 이럴거면 ANOVA를 도대체 왜 하는가? 그냥 Post Hoc을 하면 되지 않나?에 대한 질문의 답이 될 수도 있겠는데, ANOVA에서 유의하지 않은 경우에, 그냥 심심해서 Post Hoc을 해 볼 수도 있겠다고 생각이 듭니다만, 그런 경우에는 Post Hoc에서 유의한 차이를 보이는 집단 쌍을 찾더라도 그 결과는 무시할 수 있습니다. 즉, ANOVA에서 유의하지 않았다면 사후 분석 결과를 볼 필요 자체가 없습니다.

이것을 더 자세하게 이야기한다면 결국, 집단 간 차이 분석의 주 분석 결과는 ANOVA이고, 그러니까 ANOVA 결과가 먼저 제시되어야 합니다. 그 이후에 제시되는 사후 분석은 ANOVA가 유의할 때의 추가 분석이라고 보면 되겠습니다. 

그렇긴한데, ANOVA에서 유의했으나, Post Hoc에서 유의하지 않았다면 이때가 정말 난감한 경우인데, 분명 ANOVA로 차이가 있다고 발견해 놓고, 막상 어떤 집단이 서로 다른지는 찾지 못해서 난감하게 된 경우인데, 아쉽지만 이럴 떄는 집단 간 차이가 유의한 것으로만 단순 해석을 하는 것이 합리적입니다. 굳이 찾아내고 싶다면 적당히 Box Plot으로 보니 이 두 집단이 많이 다르지 않을까 하는 예상 정도로 마무리 지으면 큰 무리가 없겠습니다. 조금은 무책임하군요.


그러면, 실제로 검정을 해보자구요. ANOVA에서의 남성, 여성, 외계인의 발 길이를 예를 그대로 다시 사용해서 처음부터 다시 해 보겠습니다. 

boy = [27.3, 28.5, 29.7]
girl = [23.5, 24.2, 22.2, 25.7]
alien = [19.2, 18.6, 17.1]

 

➊ 정규성검정을 해 보겠습니다. 가장 보수적인 Shapiro-Wilk!

from scipy.stats import shapiro

boy = [27.3, 28.5, 29.7]
girl = [23.5, 24.2, 22.2, 25.7]
alien = [19.2, 18.6, 17.1] 

print(shapiro(boy))
print(shapiro(girl))
print(shapiro(alien))

>
ShapiroResult(statistic=1.0, pvalue=0.9999986886978149)
ShapiroResult(statistic=0.9968306422233582, pvalue=0.9891670346260071)
ShapiroResult(statistic=0.9423076510429382, pvalue=0.5367357134819031)

 

오 세집단 모두 정규성이 OK이군요. 사실상 너무 작은 표본 크기이기 때문에 의미는 없고, 그냥 모집단이 정규성을 가지니까 표본의 평균도 정규적이다라고 가정하는 편이 현실적입니다. 

➋ 등분산성검정을 해 보겠습니다. 옛다 Levene와 Bartlett 두 개 모두 하죠 머!

from scipy.stats import levene, bartlett

print(levene(boy, girl, alien))
print(bartlett(boy, girl, alien))

>
LeveneResult(statistic=0.19816176470588293, pvalue=0.8246838520550716)
BartlettResult(statistic=0.19083867589274103, pvalue=0.9089916798322615)

 

오, 등분산 검정도 통과! 3개의 집단이 모두 모분산이 동질할 예정입니다.  

➌ 드디어 대망의 One way ANOVA를 시행! 

from scipy.stats import f_oneway

f_oneway(boy, girl, alien)

>
F_onewayResult(statistic=47.26810344827576, pvalue=8.603395069970194e-05)

 

ANOVA F검정 결과가.. 유의하네요! 오 세집단의 평균의 차이가 있겠습니다. 

➍ 그러면 어느 집단이 다른지 확인할 수 있도록 Post Hoc을 해 보겠습니다. 표본의 크기가 서로 다르므로 Bonferroni!

일단 Bonferroni 검정을 하기 전에 데이터의 모양새가 값과 그룹의 종류에 대한 데이터를 순서를 맞추어 넣어야 합니다. 그래서 보기 편하게 pandas dataframe으로 데이터를 맞춰보면 

import pandas as pd

lst_value = boy + girl + alien
lst_group = ["boy" for i in range(len(boy))] + ["girl" for i in range(len(girl))] + ["alien" for i in range(len(alien))]
df_total = pd.DataFrame({'Group':lst_group, 'Value':lst_value}).reset_index(drop=True)
print(df_total)

>
    Group  Value
0    boy   27.3
1    boy   28.5
2    boy   29.7
3   girl   23.5
4   girl   24.2
5   girl   22.2
6   girl   25.7
7  alien   19.2
8  alien   18.6
9  alien   17.1

 

요런 식으로 데이터를 만들어줘야 Bonfrroni를 할 수 있습니다. 자 이제 고고씽.

from statsmodels.sandbox.stats.multicomp import MultiComparison
from scipy.stats import ttest_ind

comp = MultiComparison(df_total['Value'], df_total['Group'])
ret = comp.allpairtest(scipy.stats.ttest_ind, method='bonf')
print(ret[0])

>
Test Multiple Comparison ttest_ind 
FWER=0.05 method=bonf
==============================================
group1 group2   stat    pval  pval_corr reject
----------------------------------------------
 alien    boy -10.9355 0.0004    0.0012   True
 alien   girl  -5.5521 0.0026    0.0078   True
   boy   girl   4.4257 0.0069    0.0206   True
----------------------------------------------

 

pval_corr이 집단의 개수 - 여기서는 3 - 로 곱해진 보정된 p value이고, 모두 유의하군요. 그러니까 서로서로 모두 차이가 있다는 결론입니다. 호오. 편리하군요. ANOVA에서 Box plot으로 봤던 데이터 분포에 대한 결론으로 걸맞네요. 

 $\alpha := 1-(1-\alpha)^{_n C_2}$을 FamilyWise Error Rate라고 해서 적어도 하나의 집단 비교에서 Type I Error를 범할 확률이라고도 부릅니다. 그러니까 집단이 많아져서 비교를 많이 할수록 FWER이 증가합니다. 사실상 수정된 유의 수준 정도로 부르면 좋았을 텐데 하는 아쉬움이 있군요.

친절한 데이터 사이언스 강좌 글 전체 목차 (정주행 링크) -



댓글





친절한 데이터 사이언스 강좌 글 전체 목차 (정주행 링크) -