1. 분석 시나리오 설정
다음과 같은 상황을 가정해 보겠습니다.
- 상황: 한 이커머스 기업에서 4가지의 새로운 UI 디자인(A, B, C, D)을 개발했습니다.
- 데이터: 각 디자인을 경험한 고객 40명을 무작위로 추출하여 만족도 점수(1~5점)를 측정했습니다.
- 목표: 디자인 방식에 따라 고객 만족도 평균에 유의미한 차이가 있는지 검정합니다. (유의수준 0.05)
가설 설정
- 귀무가설(H0): 네 가지 UI 디자인에 따른 고객 만족도 평균은 모두 같다.
- 대립가설(H1): 적어도 한 그룹의 만족도 평균은 나머지 그룹들과 다르다.
2. 데이터 준비 및 기초 확인
먼저 구글 코랩에서 데이터를 생성하고 구조를 확인합니다.
import pandas as pd
# 4개 그룹의 고객 만족도 데이터 생성
df = pd.DataFrame({
'Design_A': [3.5, 4.3, 3.8, 3.6, 4.1, 3.2, 3.9, 4.4, 3.5, 3.3],
'Design_B': [3.9, 4.4, 4.1, 4.2, 4.5, 3.8, 4.2, 3.9, 4.4, 4.3],
'Design_C': [3.2, 3.7, 3.6, 3.9, 4.3, 4.1, 3.8, 3.5, 4.4, 4.0],
'Design_D': [3.8, 3.4, 3.1, 3.5, 3.6, 3.9, 3.2, 3.7, 3.3, 3.4]
})
print(df.head(2))
3. ANOVA 전제 조건 검정 (가정 확인)
분산분석을 수행하기 전, 데이터가 정규성과 등분산성을 만족하는지 반드시 확인해야 합니다.
① 정규성 검정 (Shapiro-Wilk)
모든 그룹의 p-value가 0.05보다 커야 정규성을 만족합니다.
from scipy import stats
print("정규성 검정 결과:")
print(f"A: {stats.shapiro(df['Design_A'])}")
print(f"B: {stats.shapiro(df['Design_B'])}")
print(f"C: {stats.shapiro(df['Design_C'])}")
print(f"D: {stats.shapiro(df['Design_D'])}")
해석: 모든 그룹의 p-value가 0.05보다 크게 나왔으므로 귀무가설을 채택하여 정규성을 만족한다고 판단합니다.
② 등분산성 검정 (Levene)
그룹 간의 분산이 동일한지 확인합니다.
print(f"등분산성 검정(Levene): {stats.levene(df['Design_A'], df['Design_B'], df['Design_C'], df['Design_D'])}")
해석: p-value가 약 0.22로 0.05보다 크므로, 모든 집단은 동일한 분산을 가진다는 가정을 만족합니다.
4. 일원 분산 분석 수행
가정을 모두 만족했으므로 본격적으로 ANOVA를 수행합니다. 두 가지 방법을 소개합니다.
방법 1: SciPy 활용 (통계량과 p-value만 빠르게 확인할 때)
f_stat, p_val = stats.f_oneway(df['Design_A'], df['Design_B'], df['Design_C'], df['Design_D'])
print(f'F-통계량: {f_stat:.4f}, p-value: {p_val:.6f}')
방법 2: Statsmodels 활용 (상세 ANOVA 테이블이 필요할 때)
이 방법은 데이터를 재구조화(melt) 해야 합니다.
자유도(df)나 제곱합(sum_sq)을 확인하려면 해당 방법을 써야 합니다..
from statsmodels.formula.api import ols
from statsmodels.stats.anova import anova_lm
# 데이터 재구조화 (Wide -> Long 형식)
df_melt = df.melt(var_name='design', value_name='score')
# OLS 모델 학습 및 ANOVA 테이블 생성
model = ols('score ~ design', data=df_melt).fit()
print(anova_lm(model))
결과 해석: p-value(PR(>F))가 0.0006으로 0.05보다 훨씬 작습니다. 따라서 귀무가설을 기각하고, "디자인에 따라 만족도 평균에 차이가 있다"는 대립가설을 채택합니다.
5. 사후 검정 (Post-hoc)
"어딘가 차이가 있다"는 것은 알았는데, 정확히 어떤 디자인끼리 차이가 나는지 궁금할 때 수행합니다.
from statsmodels.stats.multicomp import pairwise_tukeyhsd
# Tukey HSD 수행
tukey_result = pairwise_tukeyhsd(df_melt['score'], df_melt['design'], alpha=0.05)
print(tukey_result.summary())
보는 법: reject 컬럼이 True인 것을 찾으세요. 결과에서 A-B, B-D 쌍이 유의미한 차이를 보이는 것을 확인할 수 있습니다.
6. [보너스] 만약 정규성을 만족하지 못한다면?
만약 정규성 검정에서 하나라도 실패했다면, 비모수 검정인 Kruskal-Wallis 검정을 사용해야 합니다.
# 정규성이 깨진 상황을 가정한 비모수 검정
print("Kruskal-Wallis 검정:")
print(stats.kruskal(df['Design_A'], df['Design_B'], df['Design_C'], df['Design_D']))
마무리하며
오늘은 일원 분산 분석의 전체 프로세스를 코드로 살펴봤습니다.
- 정규성/등분산성 확인
- ANOVA 수행(p-value 확인)
- 사후 검정 이 세 단계를 꼭 기억해세요~
'AI' 카테고리의 다른 글
| 이원 분산 분석(Two-way ANOVA) (0) | 2026.05.14 |
|---|---|
| 분산분석 ANOVA (0) | 2026.05.13 |
| 카이제곱 검정 (0) | 2026.05.11 |
| Qdrant + NCP Clova Embedding으로 AI 기반 매칭 시스템 구축하기 (2) | 2025.12.04 |