빅데이터 분석기사 실기 3유형 (1) — 가설검정 (T-검정·ANOVA)
3유형 통계검정의 절반. 귀무·대립가설부터 단일/독립/대응 T-검정, 분산분석까지 scipy로 한 번에.
3유형은 통계 가설검정. 코드는 한 줄이지만, 가설 설정과 결과 해석에서 점수가 갈린다. 공식을 외우는 게 아니라 언제 어떤 검정을 쓰는지 를 분별하는 시험이다.
3유형은 어떤 시험인가?
| 항목 | 내용 |
|---|---|
| 배점 | 30점 (2문제 × 15점) |
| 형식 | 통계량·p-value·결론 등 여러 값을 단계적으로 답 |
| 시간 권장 | 약 40~50분 |
| 주요 도구 | scipy.stats, statsmodels |
| 출제 영역 | T-검정, 분산분석, 카이제곱, 회귀분석, 로지스틱 회귀 |
이번 글은 T-검정 + 분산분석까지. 회귀와 카이제곱은 (2)에서 다룬다.
가설검정 기초 — 무조건 외우는 4가지
1. 귀무가설(H₀) vs 대립가설(H₁)
- 귀무가설(H₀): "차이가 없다 / 효과가 없다 / 같다" — 기본 입장
- 대립가설(H₁): "차이가 있다 / 효과가 있다 / 다르다" — 입증하고 싶은 것
검정은 H₀를 기각할 충분한 증거가 있는지 본다. H₁을 직접 증명하지 않는다.
2. p-value의 해석
p-value < 0.05 → 귀무가설 기각 → 대립가설 채택 → "유의한 차이가 있다"
p-value ≥ 0.05 → 귀무가설 채택 → "차이가 있다고 보기 어렵다"
문제에서 보통 유의수준 α = 0.05 로 준다.
3. 단측 vs 양측
- 양측 검정: "A와 B가 다르다" — 방향 무관
- 단측 검정: "A가 B보다 크다(또는 작다)" — 방향 지정
4. 정규성·등분산성 — 검정 선택의 갈림길
- 정규성: 데이터가 정규분포를 따르는가? →
shapiro검정 - 등분산성: 두 집단의 분산이 같은가? →
levene검정
from scipy import stats
stats.shapiro(data) # p > 0.05 → 정규성 만족
stats.levene(group1, group2) # p > 0.05 → 등분산 만족
"정규성 검정 먼저 수행한 후~"가 문제에 나오면, 이걸 빼먹지 말 것.
T-검정 — 평균 비교의 3종 세트
(1) 단일표본 T-검정 (one-sample)
한 집단의 평균이 특정 값과 같은가?
예: "이 제품의 평균 무게가 100g과 다른지 검정하라."
H0: μ = 100
H1: μ ≠ 100
from scipy import stats
t_stat, p_value = stats.ttest_1samp(df["weight"], popmean=100)
print(f"t = {t_stat:.4f}, p = {p_value:.4f}")
if p_value < 0.05:
print("귀무가설 기각: 평균이 100과 다르다")
else:
print("귀무가설 채택: 평균이 100과 같다고 볼 수 있다")
(2) 독립표본 T-검정 (two-sample, 비대응)
서로 다른 두 집단의 평균을 비교
예: "남자와 여자의 키 평균이 다른가?"
male = df[df["gender"]=="M"]["height"]
female = df[df["gender"]=="F"]["height"]
# 등분산성 검정 먼저
_, p_levene = stats.levene(male, female)
equal_var = p_levene > 0.05
t_stat, p_value = stats.ttest_ind(male, female, equal_var=equal_var)
print(f"t = {t_stat:.4f}, p = {p_value:.4f}")
equal_var=False이면 Welch's t-test. 등분산성을 만족하지 않을 때 자동으로 보정된다.
(3) 대응표본 T-검정 (paired)
같은 대상의 변화량을 비교 — "전 vs 후"
예: "다이어트 프로그램 전후의 체중 차이가 유의한가?"
t_stat, p_value = stats.ttest_rel(df["before"], df["after"])
print(f"t = {t_stat:.4f}, p = {p_value:.4f}")
같은 사람의 전후 데이터인지 가 핵심 판별 포인트. 같은 사람이면 paired, 다른 그룹이면 ind.
단측 검정으로 바꾸기
기본은 양측. 단측을 원하면 alternative 옵션을 준다.
# H1: μ > 100 (큰 쪽)
stats.ttest_1samp(data, 100, alternative="greater")
# H1: μ < 100 (작은 쪽)
stats.ttest_1samp(data, 100, alternative="less")
# 양측 (기본)
stats.ttest_1samp(data, 100, alternative="two-sided")
분산분석 (ANOVA) — 셋 이상 집단의 평균 비교
T-검정은 두 집단까지. 세 집단 이상의 평균이 같은지 보려면 ANOVA를 쓴다.
H0: μ1 = μ2 = μ3 = ...
H1: 적어도 한 평균은 다르다
일원분산분석 (One-way ANOVA)
from scipy import stats
a = df[df["group"]=="A"]["score"]
b = df[df["group"]=="B"]["score"]
c = df[df["group"]=="C"]["score"]
f_stat, p_value = stats.f_oneway(a, b, c)
print(f"F = {f_stat:.4f}, p = {p_value:.4f}")
p < 0.05 → "적어도 한 집단의 평균이 다르다." 단, 어느 집단이 다른지는 알 수 없다. 사후검정이 필요.
statsmodels로 더 자세히 (ANOVA 표 출력)
import statsmodels.api as sm
from statsmodels.formula.api import ols
model = ols("score ~ C(group)", data=df).fit()
table = sm.stats.anova_lm(model, typ=2)
print(table)
출력에서 PR(>F) 가 p-value, F 가 F 통계량, sum_sq 가 제곱합이다.
사후검정 (Tukey HSD)
ANOVA가 유의했을 때 어느 그룹 vs 어느 그룹이 다른지 확인.
from statsmodels.stats.multicomp import pairwise_tukeyhsd
result = pairwise_tukeyhsd(df["score"], df["group"], alpha=0.05)
print(result)
reject 컬럼이 True인 쌍이 "유의하게 다른 두 그룹"이다.
정규성·등분산성을 만족하지 않을 때 — 비모수 대안
| 모수 검정 | 비모수 대안 | 함수 |
|---|---|---|
| 독립 T검정 | Mann-Whitney U | stats.mannwhitneyu |
| 대응 T검정 | Wilcoxon 부호순위 | stats.wilcoxon |
| 일원 ANOVA | Kruskal-Wallis | stats.kruskal |
시험에서 "정규성을 만족하지 않는다고 가정"하면 비모수 검정을 쓰라는 신호다.
stats.mannwhitneyu(g1, g2)
stats.wilcoxon(before, after)
stats.kruskal(a, b, c)
자주 쓰는 표 — 어떤 검정을 쓸까?
| 상황 | 검정 |
|---|---|
| 한 집단 평균 vs 특정 값 | 단일표본 T-검정 |
| 두 독립 집단 평균 비교 | 독립표본 T-검정 |
| 같은 대상의 전·후 비교 | 대응표본 T-검정 |
| 셋 이상 집단 평균 비교 | One-way ANOVA |
| ANOVA 후 어느 쌍이 다른지 | Tukey HSD |
| 정규성 X, 두 독립 집단 | Mann-Whitney U |
| 정규성 X, 대응 표본 | Wilcoxon |
| 정규성 X, 셋 이상 | Kruskal-Wallis |
실전 답안 작성 흐름 (점수 받는 패턴)
3유형은 보통 다음과 같은 방식으로 단계별 답을 요구한다.
1. 가설 설정 (H0, H1 명시)
2. 검정 수행 → 통계량, p-value 계산
3. 결론 (귀무가설 기각/채택)
답안 예시 코드 + 출력
# 문제: 그룹 A와 B의 점수 평균이 다른지 유의수준 0.05로 검정하라.
from scipy import stats
a = df[df["group"]=="A"]["score"]
b = df[df["group"]=="B"]["score"]
# (1) 등분산 검정
_, p_lev = stats.levene(a, b)
equal_var = p_lev > 0.05
# (2) T-검정
t_stat, p_value = stats.ttest_ind(a, b, equal_var=equal_var)
print(f"검정통계량 t = {round(t_stat, 4)}")
print(f"p-value = {round(p_value, 4)}")
# (3) 결론
if p_value < 0.05:
print("결론: 귀무가설 기각. 두 그룹의 평균은 유의하게 다르다.")
else:
print("결론: 귀무가설 채택. 두 그룹의 평균이 다르다고 볼 수 없다.")
시험 답안에는 소수점 자리수 지정을 자주 요구한다.
round(x, 4)같은 형식을 마지막에 꼭 챙긴다.
정리
| 검정 | scipy 함수 |
|---|---|
| 단일표본 T | stats.ttest_1samp(data, popmean) |
| 독립표본 T | stats.ttest_ind(g1, g2, equal_var=True/False) |
| 대응표본 T | stats.ttest_rel(before, after) |
| 정규성 검정 | stats.shapiro(data) |
| 등분산 검정 | stats.levene(g1, g2) |
| 일원 ANOVA | stats.f_oneway(a, b, c, ...) |
| Tukey 사후 | pairwise_tukeyhsd(values, groups) |
| 비모수 | mannwhitneyu, wilcoxon, kruskal |
검정을 언제 쓰는지의 분기 트리만 머릿속에 그려두면, 코드는 한 줄이라 무섭지 않다. 다음 글에서는 회귀분석·로지스틱 회귀·카이제곱 으로 3유형을 마무리한다.