데이터과학 기초/통계

파이썬으로 분산분석(ANOVA)하기 (1) - One Way (일원 분산분석)

eigenvector 2022. 8. 14. 21:17
반응형

분산 분석(ANOVA)으로의 확장

 

지금까지 우리는 t-test와 상관관계 분석을 수행했습니다.

 

두 방법론은 앞으로 배울 방법론의 기초가 되는 방법론으로 분산분석(ANOVA)과 회귀분석(Regression)과 깊은 관계를 갖습니다.

 

(좀 더 깊은 레벨에서 보자면, ANOVA와 Regression, 더 나아가 t-test는 모두 선형 모형이라는 큰 범주에 속해 있습니다)

 

오늘은 이 중, 실험에서 많이 사용되는 분산분석(ANOVA, Analysis of Variance)에 대해 배워보겠습니다.

 


분산분석(ANOVA, Analysis of Variance)?

분산 분석은 말 그대로 분산을 분석하는 방법론입니다.

 

그렇다면 분산(Variance)는 무엇일까요? 일단 사전적 정의로는 (개별값 - 평균)을 제곱하여 합한 뒤, 자료 개수로 나눠주면 됩니다.

 

하지만, 더 간단하게 생각하면 분산이란 "데이터가 예상(평균)과 다른 정도"를 표현한 값입니다.

 

예를 들어, 시장 조사를 위해 분식집의 떡볶이 가격을 조사했다고 해 봅시다.

 

A와 B 지역 중 임의로 5곳의 가게를 들려 떢복이 가격을 물어봤을 때,

 

A 지역에서는 가격이 [2000, 1500, 3000, 3400, 5000]이고

B 지역에서는 가격이 [3000, 3000, 3300, 2800, 2900] 일 때

 

우리는 A지역의 떡볶이 가격이 더 예측이 어렵다(=분산이 크다)는 것을 알 수 있습니다.

 

그렇다면 분산분석은 어떤 것을 확인하려 할까요?

 

분산분석의 목적은 간단합니다. 떡볶이 가격의 예측이 빗나가는 문제에서 "지역"이 유의미한 영향을 미치는지 알아보는 것입니다.

 

즉, "지역 내 가게별 가격 차이가 큰 것이 예측을 어렵게 하는지" vs "지역 사이 가격 차이가 큰 것이 예측을 어렵게 하는지" 확인하자는 것입니다.

 

통계적으로 말하면 전체 분산에서 집단내(within), 집단 간(between) 중 어느 쪽의 영향력이 큰지 검정하는 것이 분산분석의 핵심입니다.

 


일원(One-Way) 분산분석

ANOVA의 종류?

분산에 영향을 미치는 조건을 확인하는 과정에서 분산분석의 여러 분파가 생기게 됩니다.

 

  • 하나의 큰 요인을 본다(일원, One-Way ANOVA)
  • 두 개의 요인을 본다 + 그리고 두 요인이 서로 영향을 주는지도 본다(이원, Two-Way ANOVA)

물론, 여기서 끝이 아니고 ANCOVA(공분산 분석)과 같은 접근법 등 여러 종류가 있지만 나중에 다루도록 하겠습니다.

 

우리가 오늘 다룰 일원 분산분석은 말 그대로 "하나의 요인"의 영향력을 확인하는 방식입니다.

 

좀 더 우아하게 표현하면 주 효과(main effect)를 확인한다고 볼 수 있겠군요.

 


t-test vs ANOVA

 

 

"어차피 하나의 요인으로 집단이 차이가 나는 것을 확인한다면 t-test와 같지 않나요?"

 

라는 질문이 있을 수 있습니다. 하지만, 일원 분산분석은 3 그룹 이상의 차이를 확인할 수 있다는 장점을 갖습니다.

 

"그렇다면 t-test를 여러 번 돌리면 될텐데 굳이 ANOVA를 해야 하나요?"

 

라고 묻는다면, 같은 테스트를 반복할때 마다 누적되는 오류를 줄일 수 있는 장점을 갖는다 라고 말할 수 있습니다.

 

모든 통계적 검정은 오류 확률이 있는데, 반복할수록 오류 확률은 크게 증가하기 때문입니다.

 

이해가 잘 안간다면 비슷한 원리로 은행 복리에서 돈이 꾸준히 증가하는 현상을 생각해 보시면 이해가 쉽습니다.

 


t분포 vs F분포

 

이렇게, t-test와 유사하지만 확장된 논리를 가지고 있기 때문에 ANOVA에서는 t-분포 대신 F-분포를 사용합니다.

 

이유는 간단합니다. 곰곰히 생각해 보면 t분포는 "평균을 확인"할 때 사용합니다.

 

하지만, 이제 우리는 "집단 내 변동성(=분산)" vs "집단 간 변동성(=분산)" 을 확인해야 합니다.

 

즉, "비율"이 분석의 핵심이 되는 것입니다.

 

그래서 t분포는 "t(자유도)" 형태를 갖지만, F분포는 "F(자유도, 자유도)" 형태를 갖습니다.

 

(여기서 카이제곱 분포를 슬며시 넣어주면 우리가 배웠던 내용이 모두 하나로 통일되지만, 이것은 나중에 통합해서 정리하겠습니다)

 


일원 분산 분석(One-Way ANOVA)의 가정

일원 분산 분석의 가정은 간단합니다.

 

귀무가설 : 집단에 따른 차이가 존재하지 않는다

대립가설 : 집단 중 적어도 하나의 집단은 차이가 날 것이다 = 집단 중 최소 한 쌍의 집단은 차이가 존재한다

 

귀무가설은 일반적으로 비슷하지만, 대립가설은 조금씩 설명이 다른데 사실 같은 이야기를 하는 경우가 많습니다.

 

곰곰히 생각해 보면 이해를 하실 수 있다고 생각하고 넘어가겠습니다.

 

이제 실제 검정을 수행해 보도록 하죠.

 


환경 설정

기본적인 설정 환경은 거의 동일합니다. 시각화, 데이터처리, 통계 패키지 순으로 설치, 로드하면 됩니다.

 

환경 설정 및 설치 과정은 아래 포스팅을 참고하시면 되겠습니다.

 

2022.07.31 - [데이터과학 기초/Python배우기] - conda로 손쉽게 파이썬 환경 관리하자 (Intel부터 M1까지) - 기초

 

다만, 오늘은 pandas, seaborn, pingouin까지는 동일하지만 statsmodels도 사용해 보도록 하겠습니다.

 

(물론 지금까지 사용한 pingouin도 나쁘지 않지만, R 기반 유저와 연동성이 더 좋기 때문입니다)

 

# conda 사용할 경우
conda install -c conda-forge seaborn pandas statsmodels

# pip 사용할 경우
pip install seaborn pandas statsmodels

 

본 실습에서는 Jupyter 환경을 사용하지만, 본인이 선호하는 환경을 사용해도 좋습니다.

 


데이터 로드

오늘 사용할 (가상) 샘플 데이터는 제품 색상과 만족도를 조사한 내용입니다.

 

sati.csv
0.00MB

 

위 파일을 다운받아 파이썬 폴더에 넣어 주세요.

 

import pandas as pd

sati = pd.read_csv("./sati.csv")
sati.head()

 

90명의 참여자가 확인됐고,  color와 satisfaction 컬럼이 제대로 로드가 된 것 같습니다.

 

간단한 기술 분석을 해 봅시다.

 

sati.describe()

 

describe()로는 연속된 데이터의 특성만 잡아낼 수 있기에, 추가적으로 카테고리 형태의 데이터의 특성을 확인해 봅시다.

 

sati.Color.value_counts()

 

각 색상 당 30명씩 할당되어 있다는 것이 확인됩니다.

 


 

일원 분산분석(One Way ANOVA)의 경우, 그룹별 값의 분산을 비교해야 하기 때문에 카테고리별로 데이터의 특징을 파악하는 것이 중요합니다.

 

좀 더 디테일한 레벨에서 "각 색상 수준"에서 데이터셋을 나눠서 특성을 확인하는 코드를 구성해 보겠습니다.

 

필터링 코드는 다음과 같이 구성합니다.

 

데이터셋[데이터셋필터조건]

# 필터 조건은 조건식으로 구성 가능
데이터셋[데이터셋.컬럼명 == 조건] or 데이터셋[데이터셋.컬럼명 > 조건] 과 같은 식으로.

 

Python의 반복명령어(for~in)으로 구체화 해 봅시다.

 

아래 코드는 반복적으로 데이터를 필터링 해서 describe()를 적용합니다.

 

for col in ["Red","Black",'White']:
    print(col)
    print(sati[sati.Color == col].describe())
    print()

 

각 색상별 만족도의 특성을 확인했지만, 직관적으로 이해하기 어렵습니다.

 

이 문제를 해결하기 위해 시각화를 사용해 보도록 하겠습니다.

 


데이터 시각화

그 다음으로, 데이터의 구체적인 특징을 파악하기 위해 시각화를 수행하겠습니다.

 

시각화 방식은 1) 박스플롯 2) 밀도 함수가 존재합니다.

 

박스플롯의 경우, 이미 과거 포스팅에서 다룬 바가 있기 때문에 오늘은 밀도 함수를 구성해 보겠습니다.

 

우리는 "각 제품 특성(색상)" 에 따라 데이터의 분포가 어떻게 형성되는 지 확인하는 것이 목표이므로 밀도함수에 색상별 구분 파라메터를 넣어주면 됩니다.

 

이 경우, 표시하려는 값은 만족도(Satisfaction), 구분은 색상(Color)이 되겠습니다.

 

이 구성에 따라, 다음과 같이 코드를 구성합니다.

 

import seaborn as sns

sns.displot(data=sati, x="Satisfaction", hue="Color", kind="kde")

 

 

 

얼핏 보기에는 흰색을 제외한 색의 만족도가 거의 유사해 보이는군요.

 

또한, 검은색 상품을 제외하고 종 모양의(Bell-Shaped) 정규분포 형태를 만족하는 것 처럼 보입니다.

 

확실한 차이점을 확인하기 위해 통계적 검정을 수행할 차례입니다.

 


가정 검정

반복적으로 설명한 바와 같이, 모수를 기반으로 하는 통계모형(Parametric)은 특정한 조건, "가정"을 바탕으로 합니다.

 

반대로 모수를 기반으로 하지 않는 모형(Non-Parametric)은 "가정"이나 요구사항이 없는 대신 설명의 제한이나 약한 검정력을 보이는 경우가 많습니다.

 

2022.08.11 - [데이터과학 기초/통계] - 파이썬으로 상관관계(Correlation analysis) 분석하기 : 피어슨의 r

2022.08.12 - [데이터과학 기초/통계] - 파이썬으로 상관관계(Correlation analysis) 분석하기 : 스피어만의 rho / 켄달의 tau

 

One-Way ANOVA는 후술할 회귀분석과 더불어 모수를 기반으로 하는 검정이기 때문에 데이터의 조건을 만족할 필요가 있습니다.

 

여러가지 조건이 있지만, 가장 크게 확인해야 될 부분은 두 가지로 1) 정규성 검정과 2) 등분산 검정이 존재합니다.

 

이 두가지 조건을 구체적으로 확인해 보도록 하겠습니다.

 


정규성 검정(Normality Test)

 

정규성 검정의 경우, 앞선 여러 포스팅에서 검정하는 방식을 보여줬기 때문에 특별히 설명할 내용을 없습니다.

 

단, 각 그룹별로 검정하는 과정을 편하게 하기 위해 리스트로 값을 통합하여 검정하기 위해 다음과 같이 코드를 구성했습니다.

 

잘 이해가 되지 않는다면 앞선 시각화 코드를 다시 확인해 주세요.

 

groups = []

for color in ['Red','White','Black']:
    groups.append(list(sati[sati.Color==color]['Satisfaction']))
    

# 각 그룹별 검정 코드 (앞의 리스트 순서대로 확인 가능)
pg.normality(groups[0])

pg.normality(groups[1])

pg.normality(groups[2])

 

제가 코딩한 환경은 Jupyter기 때문에 각 셀에 값을 할당하여 확인해 보겠습니다.

 

 

정규성 검정의 경우 p-value의 확인이 <0.05 조건의 반대라는 점을 유의하면 됩니다.

 

세 그룹 모두 정규성을 만족하기 때문에 다음 등분산 검정을 진행하도록 하겠습니다.

 

 


등분산 검정(Equality of Variance, Homogeneity of variance)

등분산 검정은 각 집단의 분산이 같은지 확인하는 과정입니다.

 

이 조건을 만족하지 못할 경우, 다른 방식의 ANOVA(Welch의 검정)를 사용하게 됩니다.

 

(등분산 검정은 t-test부터 제시된 개념이지만, 쉽게 이해하려면 회귀분석과 같이 보는 게 좋기 때문에 나중에 정리해보겠습니다)

 

크게, Levene과 Bartlett의 방식 두 가지가 존재합니다.

 

일반적으로 Levene's의 등분산이 덜 민감하지만, 정규분포 조건을 만족한 데이터라면 Bartlett검정을 더 추천합니다.

 

Levene의 검정은 다음과 같이 쉽게 구할 수 있습니다.

 

import pingouin as pg

pg.homoscedasticity(sati, dv="Satisfaction", group="Color") # 등분산 가정 만족

 

 

Bartlett의 검정은 조금 까다로운데요, 리스트 형태를 재구성해서 다음과 같이 수행할 필요가 있습니다.

 

리스트 형태를 재구성하는 방법은 앞선 시각화에 나온 데이터 그룹별 필터링 방식의 응용입니다.

 

잘 이해가 되지 않는다면, 앞의 코드를 재확인 해 주세요.

 

import pingouin as pg

groups = []

for color in ['Red','White','Black']:
    groups.append(list(sati[sati.Color==color]['Satisfaction']))
    
pg.homoscedasticity(groups, method="bartlett", alpha=.05)

 

 

등분산 검정 또한, p-value가 0.05보다 커야 한다는 사실을 유의해 주세요.

 

검정을 수행한 결과, 두 검정 모두 동등한 분산인 것이 확인됐습니다.

 

이제 다음 단계인 ANOVA의 구체적인 적용으로 넘어가 보겠습니다.

 


ANOVA 검정

파이썬에서 ANOVA의 검정을 지원하는 패키지는 Scipy, Statsmodels, Pingouin 등이 존재합니다.

 

다만, Scipy는 인터페이스나 해석이 어려운 편이며, 공학 계열의 분석에 편리하게 구성되어 있습니다.

 

그렇기에, R의 문법을 기반으로 하는 statsmodels와, 독립적인 문법을 가진 Pingouin을 추천하는 편입니다.

 

 

Statsmodels와 Pingouin

 

다만, 뒷부분에서 다룰 사후검정(Post-doc Test)에서는 statsmodels가 상대적으로 해석이 편리한 면이 있습니다.

 

또한 R과 연동해서 사용한다면 statsmodels의 문법이 더 편리한 편이 많기에 오늘은 statsmodels의 검정을 사용하겠습니다.

 

이에 따라 다음과 같이 코드를 구성합니다.

 

Statsmodels의 경우 "(선형) 모형식"을 구성한 후, 연산(fit)하고 해석(anova_lm)하는 과정으로 통계를 처리합니다.

 

모형식의 구성은 간단하게

 

[좌측, 종속변수 혹은 결과값] ~ [독립변수]

 

과 같이 구성하면 됩니다. 어렵게 생각할 필요 없이 다음 코드를 확인해 보면 쉽게 이해 할 수 있습니다.

 

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

model = ols('Satisfaction ~ Color', sati).fit()
anova_lm(model)

 

Statsmodels을 사용할 때, 결과값은 dataframe 형태기 때문에 똑같이 round를 써서 소수점 정리가 가능합니다.

 

anova_lm(model).round(3)

 

결과를 보면 알겠지만, t-test와 달리 ANOVA 모형은 꽤 복잡한 해석 과정이 필요합니다.

 

다음 챕터에서 천천히 확인해 보도록 하죠.

 


ANOVA 모형의 해석

앞에서 이야기했지만, ANOVA모형은 기본적으로 "분산의 비율"로 검정을 수행합니다.

 

이 말을 풀어보자면 [(대상1) vs (대상2) = 분석결과] 크게 3가지 요인이 필요하다는 말입니다.

 

하나씩 결과표에서 꺼내보도록 하겠습니다.


검정결과(F값)

여기서 분석결과는 F(2,87) = 82.603과 같습니다.

 

F안에는 자유도(df, degrees of freedom)이 들어가며 비교 대상이 (집단 내 vs 집단 간)이기 때문에 자유도가 2개 들어갑니다.

 

F값과 자유도, 각각의 위치는 다음과 같습니다.

 

자유도와 F값의 위치

그리고, 이렇게 제시한 F값 검정의 결과는 p<0.05와 같기에 통계가 유의하다고 볼 수 있습니다.

 

p-value의 위치

 

분산의 합(Sum of Squares)과 비율

 

그럼 이 F값은 어디서 나온 것일까요? 앞서 말했듯이 "분산의 비율"을 생각해 본다면 쉽게 이해할 수 있습니다.

 

"집단 간 vs 집단 내"라는 점에서 바라볼 때, 다음과 같은 수치를 비교한 결과입니다.

 

분산의 비교로써 합(sum)이 아닌 평균(mean)을 비교했다는 점을 기억해 주세요!

 

결국 F는 두 분산(평균)의 비율(!)

 

간단하게 말해 21.134/0.256 = 82.5546875 가 F값의 연산 결과와 같다는 것입니다.

 


이제 우리는 ANOVA결과 F값과 통계적 유의성(귀무가설을 기각하고, 대립가설을 채택)을 확인할 수 있게 됐습니다.

 

그러나, 아직 ANOVA을 통해 확인하려는 사실(해석)이 남아있습니다.

 


사후검정(Post Hoc)으로 ANOVA 해석하기

 

이제 한숨 돌렸지만, 갈 길이 아직 많이 남았습니다.

 

앞서 설명한 가설 부분의 기억을 다시 떠올려 봅시다.

 

p-value가 0.05보다 작기 때문에 다음과 같이 검정 결과를 이해할 수 있습니다.

 

귀무가설 : 집단에 따른 차이가 존재하지 않는다 (귀무가설 기각)

대립가설 : 집단 중 적어도 하나의 집단은 차이가 날 것이다 = 집단 중 최소 한 쌍의 집단은 차이가 존재한다

 

그런데, 여기서 "적어도 하나의 집단" 혹은 "최소 한 쌍"은 어떻게 검증할까요?

 

우리가 검증한 내용은 "모든 집단의 비교 중에서 적어도 하나는 다를 것이다"지 "모든 집단이 다르다"나 "특정 집단만 다르다"가 아닙니다.

 

하지만, 우리가 쓰려는 보고서에는 데이터의 모든 해석이 기록되어야 합니다.

 

즉, "모든 집단을 한번씩 다 비교해야" 검증 과정이 마무리 되는 것입니다.

 

이것을 사후검정(Post Hoc Test)라고 부릅니다.

 

다시 모든 집단 묶음(Pair)을 하나씩 비교해야 한다는 것이죠.

 


사후검정의 논리

"잠깐, 아까는 t-test를 반복(두 집단의 비교)하면 오류가 올라간다고 하지 않았나요?"

 

라는 질문을 던진다면, 정확한 질문입니다.

 

우리가 하려는 사후검정은 t-test를 반복하는 것에서 오류를 보정하는 과정을 거친 방식입니다.

 

이 부분은 다양한 방법이 있는데, 가장 유명한 방식은 Tukey의 방식입니다.

 

(좀 더 강력한 검정 방식으로는 Bonferroni의 방식이 있지만 일반적으로는 Tukey의 방식을 많이 채택합니다)

 

이 부분은 다음과 같이 코드를 구성하면 됩니다.

 

from statsmodels.stats.multicomp import pairwise_tukeyhsd

posthoc = pairwise_tukeyhsd(endog = sati.Satisfaction, groups = sati.Color, alpha=0.05)

print(posthoc)

결과가 너무 정렬되지 않은 느낌이라면 다음과 같이 데이터 테이블 형태로 재구성 할 수 있습니다.

 

posthoc.summary()

해석은 간단합니다. 귀무가설을 reject(기각)할 때, 차이가 유의하기 때문에 reject 컬럼에서 True가 나오는 조합이 차이가 나는 집단입니다.

 

meandiff 컬럼은 얼마나 (평균) 차이가 나는지를 보여준다고 해석하면 됩니다.

 

간단히 말해, 세 쌍의 비교에서 각각 meandiff만큼의 차이를 보이지만, 통계적으로 "다르다"라고 말할 수 있는 조합은 아래 두 조합입니다.

 

이제 우리는 1) Black~White 2) Red~White 집단이 유의한 차이가 나는 집단이고 1) Black~Red 집단은 차이가 나지 않는다는 사실을 알았습니다.

 

즉, "하나의 집단은 차이가 난다"는 결과를 구체적으로 보자면 "세 개의 조합중 유의한 조합 2개가 존재한다"는 사실을 확인한 셈입니다.

 

이제 모든 검정 과정이 마무리됐습니다.

 


ANOVA 결과 보고하기

결과 보고는 지금까지 우리가 한 모든 분석을 한 문단으로 정리하면 되겠습니다.

 

1. 검정 방식

일원 분산분석(One-Way ANOVA)

 

2. 결과

F(2, 87) = 82.603, p-value < 0.05에서 유의하다. 세 집단의 쌍(Pair)중 적어도 하나는 차이가 존재한다(귀무가설 기각)

(앞서 제시한 F값이 있는 테이블을 같이 첨부해야 합니다)

 

3. 해석

Tukey의 사후 검정을 실행한 결과 ~집단과 ~집단이 차이가 나는 것을 확인했다. ~ 집단과 ~ 집단의 차이는 유의하지 않았다.

(앞서 제시한 Post Hoc 결과를 제시합니다. 논문 분량에 따라서는 신뢰구간이 있는 바 차트를 첨부하기도 합니다)

 


 

오늘은 간단한(?) One-Way ANOVA를 수행해 봤습니다.

 

사실, 이 외로도 ANOVA에서 다뤄야 할 몇 가지 중요한 문제들이 존재하는데요.

 

  • ANOVA 검정의 효과(Effect) 측정
  • ANOVA 검정의 대비(Contrast) 구성
  • ANOVA 결과의 시각화

와 같은 문제들이 존재합니다.

 

이 부분은 통계 전문 서적을 확인하시거나, 앞으로의 포스팅 내용을 참조하면 될 것 같습니다.

반응형