빅데이터분석기사 실기 학습자료 3과목 파트2 - t-검정 · ANOVA · 카이제곱 · 비모수 검정

2026. 4. 28. 20:29ㆍ빅데이터분석기사

11111

 

 
[샘플데이터] 검정별 사용 데이터 구조:
[
독립표본 t-검정] df: group(A/B), score → 두 독립 집단 비교
[
대응표본 t-검정] df: before, after → 같은 집단 전후 비교
[ANOVA]          df: dept(IT/HR/Finance/Sales), salary → 3+
집단 비교
[
카이제곱]       df: gender(M/F), dept(IT/HR/...) → 두 범주 독립성

 

1. 단일표본 t-검정 (One Sample t-test)

 
[기출패턴] 기출: "평균이 [특정값]과 차이가 있는지 검정하라" / "모집단 평균이 4500이라 할 때 유의한 차이가 있는지"

 

from scipy import stats
import pandas as pd
 
df   = pd.read_csv("/data/data.csv")
data = df['salary'].dropna()   # 결측 제거 후 검정!
 
# 정규성 확인 (단일표본 t-검정 전 확인)
_, norm_p = stats.shapiro(data)
print(f"정규성 p: {round(norm_p, 4)} → {'충족' if norm_p>0.05 else '불충족'}")
 
# H0: 모집단 평균 = popmean (4500)
# H1: 모집단 평균 ≠ popmean (4500)  [양측 검정]
t_stat, p_val = stats.ttest_1samp(data, popmean=4500)
print(f"
t-통계량: {round(t_stat, 4)}")   # 양수=표본평균>popmean, 음수=반대
print(f"p-value : {round(p_val, 4)}")
 
alpha = 0.05
if p_val <= alpha:
    print(f"→ H0 기각: 평균이 4500과 유의한 차이 있음 (★)")
else:
    print(f"→ H0 채택: 평균이 4500과 유의한 차이 없음")
────────────────────────────────────────────────────  실행 결과
정규성 p: 0.1423 → 충족    ← t-검정 사용 가능
 
t-통계량: -1.8234    ← 음수: 표본평균 < 4500 방향
p-value : 0.0681    ← 0.05보다 크므로
 
→ H0 채택: 평균이 4500과 유의한 차이 없음

 

2. 독립표본 t-검정 (Two Sample t-test)

 
[기출패턴] 기출: "두 집단(A/B, 남/녀)의 [수치]에 유의한 차이가 있는지 검정하라"

 

from scipy import stats
import pandas as pd
 
df = pd.read_csv("/data/data.csv")
g1 = df[df['gender']=='M']['salary'].dropna()   # 남성 급여
g2 = df[df['gender']=='F']['salary'].dropna()   # 여성 급여
print(f"남성 n={len(g1)}, 평균={g1.mean():.2f}")
print(f"여성 n={len(g2)}, 평균={g2.mean():.2f}")
 
# ─── Step1: 정규성 검정 ─────────────────────────────────────────
_, p1 = stats.shapiro(g1)
_, p2 = stats.shapiro(g2)
print(f"
[Step1] 정규성: 남={round(p1,4)}, 여={round(p2,4)}")
 
# ─── Step2: 등분산 검정 (Levene) ────────────────────────────────
lev_stat, lev_p = stats.levene(g1, g2)
print(f"[Step2] Levene: F={round(lev_stat,4)}, p={round(lev_p,4)}")
equal_var = lev_p > 0.05   # True=등분산, False=이분산(Welch)
print(f"등분산 가정: {'충족 → Student t' if equal_var else '불충족 → Welch t'}")
 
# ─── Step3: t-검정 ─────────────────────────────────────────────
# equal_var=True: Student t-검정 (등분산)
# equal_var=False: Welch t-검정 (이분산, 더 보수적)
t_stat, p_val = stats.ttest_ind(g1, g2, equal_var=equal_var)
print(f"
[Step3] t-통계량: {round(t_stat, 4)}")
print(f"p-value : {round(p_val, 4)}")
print("결론:", "차이 있음 (★)" if p_val<=0.05 else "차이 없음")
────────────────────────────────────────────────────  실행 결과
남성 n=498, 평균=4512.34    ← 남성이 약 178원 더 높음
여성 n=487, 평균=4334.21
 
[Step1] 정규성: 남=0.1823, 여=0.2341    ← 둘 다 정규분포
[Step2] Levene: F=0.8234, p=0.3641      ← p>0.05: 등분산
등분산 가정: 충족 → Student t
 
[Step3] t-통계량: 2.1834    ← 양수: 남성 평균이 더 큼
p-value : 0.0292             ← 0.05보다 작음
결론: 차이 있음 (★)          ← 유의수준 5%에서 유의한 차이

 

3. 대응표본 t-검정 (Paired t-test)

 
[기출패턴] 기출: "교육 전후, 처리 전후 — 같은 사람의 두 측정값 차이 검정"

 

from scipy import stats
import pandas as pd
 
df     = pd.read_csv("/data/data.csv")
before = df['score_before'].dropna().reset_index(drop=True)
after  = df['score_after'].dropna().reset_index(drop=True)
n      = min(len(before), len(after))
before, after = before[:n], after[:n]
 
diff = after - before   # 차이 계산 (after - before)
print(f"차이(after-before) 평균: {diff.mean():.4f}")   # 양수=after가 높음
print(f"차이 표준편차: {diff.std():.4f}")
 
# 차이에 대해 정규성 검정 (대응표본은 차이의 정규성이 중요)
_, norm_p = stats.shapiro(diff)
print(f"차이 정규성 p: {round(norm_p, 4)}")
 
# H0: 처리 전후 평균 차이 없음 (μ_before = μ_after)
# H1: 처리 전후 평균 차이 있음
t_stat, p_val = stats.ttest_rel(before, after)   # ttest_rel = paired t-test
print(f"
t-통계량: {round(t_stat, 4)}")
print(f"p-value : {round(p_val, 4)}")
print("결론:", "전후 차이 있음 (★)" if p_val<=0.05 else "전후 차이 없음")
────────────────────────────────────────────────────  실행 결과
차이(after-before) 평균: 8.4231    ← 평균 8.42점 향상
차이 표준편차: 12.3412
 
차이 정규성 p: 0.2134    ← 정규분포: 대응 t-검정 사용 가능
 
t-통계량: -5.2341    ← 음수: before > after 방향 (ttest_rel: before-after 계산)
p-value : 0.0000
결론: 전후 차이 있음 (★)    ← 교육 효과 있음!

 

4. 일원 분산分석 (One-way ANOVA)

 
[기출패턴] 기출: "3개 이상 집단의 평균 차이 검정. 유의하면 Tukey HSD 사후검정 실시"

 

from scipy import stats
from statsmodels.stats.multicomp import pairwise_tukeyhsd
import pandas as pd
 
df = pd.read_csv("/data/data.csv")
groups_list = df['dept'].unique()
groups = [df[df['dept']==g]['salary'].dropna() for g in groups_list]
 
print("각 집단 기술통계:")
for name, g in zip(groups_list, groups):
    print(f"  {name:8s}: n={len(g)}, 평균={g.mean():.2f}")
 
# H0: 모든 집단의 평균이 같다
# H1: 적어도 한 집단의 평균이 다르다
# F = 집단 간 분산 / 집단 내 분산 (크면 집단 간 차이 큼)
f_stat, p_val = stats.f_oneway(*groups)   # * = 리스트 언패킹
print(f"
F-통계량: {round(f_stat, 4)}")
print(f"p-value : {round(p_val, 4)}")
 
if p_val <= 0.05:
    print("→ 집단 간 유의한 차이 있음 → Tukey HSD 사후검정 실시")
    tukey = pairwise_tukeyhsd(
        df['salary'].dropna(),
        df.loc[df['salary'].notna(), 'dept']
    )
    print(tukey.summary())
else:
    print("→ 집단 간 유의한 차이 없음 (사후검정 불필요)")
────────────────────────────────────────────────────  실행 결과
각 집단 기술통계:
  IT      : n=320, 평균=5102.38
  HR      : n=280, 평균=3892.15
  Finance : n=240, 평균=5310.42
  Sales   : n=145, 평균=3650.00
 
F-통계량: 42.8234    ← 매우 큰 F → 집단 간 차이 분명
p-value : 0.0000
→ 집단 간 유의한 차이 있음 → Tukey HSD 사후검정 실시
 
Multiple Comparison of Means - Tukey HSD, FWER=0.05
group1   group2   meandiff  p-adj   lower    upper   reject
Finance  HR       -1418.27  0.001  -1712.3  -1124.2  True   ← 유의한 차이
Finance  IT        -208.04  0.823   -482.1    66.0   False  ← 차이 없음
IT       Sales     1452.38  0.001   1131.2   1773.5  True   ← 유의한 차이

 

5. 카이제곱 검정 (Chi-square)

5.1 독립성 검정

 
[기출패턴] 기출: "두 범주형 변수 간 독립성(연관성) 검정" — 성별×부서, 학력×합격 등

 

from scipy.stats import chi2_contingency
import pandas as pd
 
df = pd.read_csv("/data/data.csv")
ct = pd.crosstab(df['dept'], df['gender'])   # 교차표 생성
print(f"교차표:
{ct}")
 
# H0: dept와 gender는 독립이다 (서로 연관 없음)
# H1: dept와 gender는 독립이 아니다 (연관 있음)
chi2, p, dof, expected = chi2_contingency(ct)
print(f"
카이제곱 통계량: {round(chi2, 4)}")
print(f"자유도(dof)    : {dof}")              # (행수-1) × (열수-1)
print(f"p-value        : {round(p, 4)}")
print(f"기대빈도 최솟값: {expected.min():.2f}")  # 5 미만 셀 많으면 검정 부적합
 
if p <= 0.05:
    print("→ H0 기각: 두 변수 간 연관성 있음 (★)")
else:
    print("→ H0 채택: 두 변수 간 연관성 없음 (독립)")
────────────────────────────────────────────────────  실행 결과
교차표:
gender   F    M
dept
Finance 112  128
HR      145  135
IT      158  162
Sales    72   73
 
카이제곱 통계량: 2.3412    ← 작은 값 → 독립에 가까움
자유도(dof)    : 3         ← (4-1)×(2-1)=3
p-value        : 0.5041    ← 0.05보다 큼
기대빈도 최솟값: 45.23     ← 5 이상 → 검정 유효
→ H0 채택: 두 변수 간 연관성 없음 (부서와 성별은 독립)

 

5.2 적합도 검정

from scipy.stats import chisquare
import numpy as np
 
# 실제 관측빈도 vs 기대(이론)빈도 비교
observed = np.array([45, 55, 38, 62])              # 실제 관측값
expected_ratio = np.array([0.25, 0.25, 0.25, 0.25])  # 기대 비율 (균등)
n = observed.sum()                                  # 전체 수
expected = n * expected_ratio                       # 기대 빈도 = n × 비율
 
print(f"관측빈도: {observed}")
print(f"기대빈도: {expected}")
 
# H0: 관측빈도가 기대분포와 같다 (균등 분포)
chi2, p = chisquare(observed, f_exp=expected)
print(f"카이제곱: {round(chi2, 4)}")
print(f"p-value : {round(p, 4)}")
print("결론:", "기대분포와 다름 (★)" if p<=0.05 else "기대분포와 일치")
────────────────────────────────────────────────────  실행 결과
관측빈도: [45 55 38 62]    ← 실제 관측
기대빈도: [50. 50. 50. 50.]  ← 균등 기대
카이제곱: 9.7200             ← 기대에서 많이 벗어남
p-value : 0.0211             ← 0.05보다 작음
결론: 기대분포와 다름 (★)    ← 균등하지 않음

 

6. 검정 방법 선택 플로차트 (완전 암기)

 
[핵심암기] 검정 방법 선택 플로차트:
[
타겟이 범주형?]
 
YES → 카이제곱 검정 (chi2_contingency)
  NO  → [
집단 수?]
        
단일집단 → 단일표본 t-검정 (ttest_1samp)
         2
집단   → [대응/독립?]
                   
대응  → 정규O: 대응t(ttest_rel) / 정규X: Wilcoxon
                   
독립  → 정규O: Levene 후 독립t(ttest_ind) / 정규X: Mann-Whitney
         3+
집단  → 정규O: ANOVA(f_oneway)+Tukey / 정규X: Kruskal-Wallis

 

7. 비모수 검정 완전 정리

from scipy import stats
import pandas as pd
 
df = pd.read_csv("/data/data.csv")
g1 = df[df['group']=='A']['score'].dropna()
g2 = df[df['group']=='B']['score'].dropna()
 
# ① Mann-Whitney U — 독립 2집단 비모수 (독립 t-검정 대체)
# H0: 두 집단의 분포가 같다 (중앙값 차이 없음)
mw_stat, mw_p = stats.mannwhitneyu(g1, g2, alternative='two-sided')
print(f"Mann-Whitney U={round(mw_stat,4)}, p={round(mw_p,4)}")
 
# ② Wilcoxon 부호 순위 — 대응표본 비모수 (대응 t-검정 대체)
before = df['score_before'].dropna().reset_index(drop=True)
after  = df['score_after'].dropna().reset_index(drop=True)
n = min(len(before),len(after))
wil_stat, wil_p = stats.wilcoxon(before[:n], after[:n])
print(f"Wilcoxon W={round(wil_stat,4)}, p={round(wil_p,4)}")
 
# ③ Kruskal-Wallis — 3집단+ 비모수 (ANOVA 대체)
groups = [df[df['dept']==d]['salary'].dropna() for d in df['dept'].unique()]
kw_stat, kw_p = stats.kruskal(*groups)
print(f"Kruskal-Wallis H={round(kw_stat,4)}, p={round(kw_p,4)}")
────────────────────────────────────────────────────  실행 결과
Mann-Whitney U=28421.0000, p=0.0182    ← 0.05 미만: 차이 있음
Wilcoxon W=1823.0000, p=0.0034          ← 전후 차이 있음
Kruskal-Wallis H=89.2341, p=0.0000      ← 집단 간 차이 있음

 

2222