표본 데이터를 이용해 모집단의 특성(모수)에 대한 가설을 세우고, 그 가설이 맞는지 통계적으로 판단하는 절차.
| 용어 | 의미 |
|---|---|
| 귀무가설 | 차이(효과)가 없다는 주장 |
| 대립가설 | 차이(효과)가 있다는 주장 |
| 유의수준 | 귀무가설이 맞는데 기각할 확률 (보통 0.05) |
| 검정통계량 | 표본으로 계산한 통계값 (t, z, F, χ² 등) |
| p-value | 귀무가설이 참일 때, 현재 데이터보다 극단적인 결과가 나올 확률 |
1. 가설 설정
2. 유의수준() 설정
3. 데이터 수집 & 검정통계량 계산
4. p-value 계산
5. 판단
| 구분 | 설명 | 예시 |
|---|---|---|
| 양측검정 | 양방향으로 차이를 검정 | 남녀 평균 키가 다른지 |
| 단측검정 | 한쪽 방향만 검정 | 남자 평균 키가 여자보다 큰지 |
| 오차 유형 | 의미 |
|---|---|
| 제1종 오류 (Type I Error) | 실제로는 이 참인데 기각 → false positive |
| 제2종 오류 (Type II Error) | 실제로는 이 참인데 채택 → false negative |
가설검정을 수행하기 전, 데이터가 다음 조건을 만족하는지 확인해야 함.
| 상황 | 검정 방법 | 가정 |
|---|---|---|
| 모집단 σ 알 때, n≥30 | Z-검정 | 정규성 가정 완화 가능 |
| 모집단 σ 모를 때, 두 집단 비교 | t-검정 | 정규성, 등분산성(스튜던트 t) |
| 등분산성 위배 시 | Welch t-검정 | 정규성 |
| 3개 이상 평균 비교 | ANOVA | 정규성, 등분산성 |
| 범주형 vs 범주형 | 카이제곱 검정 | 기대도수 ≥ 5 |
| 정규성 위배, 2집단 | Mann–Whitney U 검정 | 비모수 |
| 정규성 위배, 3집단 이상 | Kruskal–Wallis 검정 | 비모수 |
t-검정은 표본에서 추정한 평균이 가설(모집단 평균, 두 집단 평균 차이 등)과 유의하게 다른지를 검정하는 통계 방법입니다.
모집단 분산을 모르는 상황에서 표본분산으로 추정하기 때문에 정규분포 대신 t-분포를 사용합니다.
| 종류 | 목적 | 예시 |
|---|---|---|
| 단일표본 t-검정 | 표본 평균이 특정 값 μ₀와 다른지 검정 | "우리 고객 평균 월요금이 70달러와 다른가?" |
| 독립표본 t-검정 | 두 집단 평균 비교 (서로 독립) | "Churn=Yes vs No의 월요금 차이가 있는가?" |
| 대응표본 t-검정 | 같은 집단의 전·후 비교 | "프로모션 전후 고객 요금 변화가 있는가?" |
Tip: 독립표본에서는 Welch t-test(등분산 미가정)를 기본으로 쓰는 것이 안전합니다.
t-검정의 기본 가정 중 하나는 표본(또는 표본 차이)가 정규분포를 따른다는 것입니다.
독립표본 t-검정(특히 Student t-test)에서는 두 집단의 분산이 동일하다는 가정을 합니다.
import kagglehub
import os
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from scipy import stats
# 데이터 로드
path = kagglehub.dataset_download("blastchar/telco-customer-churn")
churn = pd.read_csv(path + '/' + os.listdir(path)[0])
# 그룹 나누기
yes = churn.loc[churn['Churn']=='Yes', 'MonthlyCharges'].astype(float).dropna()
no = churn.loc[churn['Churn']=='No', 'MonthlyCharges'].astype(float).dropna()
# 1. 정규성 검사 (Shapiro-Wilk)
shapiro_yes = stats.shapiro(yes.sample(500, random_state=42)) # 표본 500개
shapiro_no = stats.shapiro(no.sample(500, random_state=42))
print(f"Shapiro-Wilk (Yes): W={shapiro_yes[0]:.4f}, p={shapiro_yes[1]}")
print(f"Shapiro-Wilk (No) : W={shapiro_no[0]:.4f}, p={shapiro_no[1]}")
Shapiro-Wilk (Yes): W=0.9329, p=3.398378854799905e-14
Shapiro-Wilk (No) : W=0.9116, p=1.756265906105648e-16
Shapiro-Wilk p-value < 0.05
→ 정규성 가정 위배 가능성 있음
그러나 표본 수가 크면 작은 왜도/첨도 차이에도 p<0.05가 되므로 QQ-Plot 시각적 확인 병행 필요
Telco 데이터에서는 약간의 비정규성(꼬리 두꺼움)이 있으나, 표본 크기가 커서 Welch t-test 적용에 큰 문제 없음
# 2. 등분산성 검사 (Levene test)
levene_stat, levene_p = stats.levene(yes, no, center='median')
print(f"Levene test: stat={levene_stat:.4f}, p={levene_p}")
Levene test: stat=361.8445, p=1.0261244899421871e-78
Levene test p-value < 0.05
→ 등분산성 위배 → Welch t-test 사용이 적절
# 3. Welch t-test
t_stat, p_val = stats.ttest_ind(yes, no, equal_var=False)
# 평균 차이 및 95% CI
mean_diff = yes.mean() - no.mean()
se = np.sqrt(yes.var(ddof=1)/len(yes) + no.var(ddof=1)/len(no))
df = (yes.var(ddof=1)/len(yes) + no.var(ddof=1)/len(no))**2 / (
(yes.var(ddof=1)/len(yes))**2/(len(yes)-1) + (no.var(ddof=1)/len(no))**2/(len(no)-1))
alpha = 0.05
t_crit = stats.t.ppf(1-alpha/2, df)
ci_low, ci_high = mean_diff - t_crit*se, mean_diff + t_crit*se
print(f"Welch t = {t_stat:.4f}, df ≈ {df:.1f}, p = {p_val:.3e}")
print(f"Mean diff (Yes-No) = {mean_diff:.3f}")
print(f"95% CI = [{ci_low:.3f}, {ci_high:.3f}]")
Welch t = 18.4075, df ≈ 4135.8, p = 8.592e-73
Mean diff (Yes-No) = 13.176
95% CI = [11.773, 14.580]
Welch t = 18.4075 → 평균 차이가 표준오차 대비 매우 큼
df ≈ 4135.8 → Welch–Satterthwaite 근사 자유도, 표본이 크기 때문에 df가 상당히 큼
p = 8.592 × 10⁻⁷³ → 귀무가설(평균 차이 없음)을 기각할 충분한 통계적 증거
Mean diff (Yes–No) = 13.176 → Churn=Yes 고객의 평균 월요금이 No보다 약 13.18달러 높음
95% CI = [11.773, 14.580] → 모평균 차이가 95% 신뢰수준에서 이 범위 안에 있을 것, CI에 0이 포함되지 않으므로 차이는 통계적으로 유의
# 4. 시각화
plt.figure(figsize=(8,5))
sns.boxplot(x='Churn', y='MonthlyCharges', data=churn, palette='Set2')
plt.title('MonthlyCharges by Churn')
plt.show()
# QQ-plot
fig, ax = plt.subplots(1,2, figsize=(8,4))
stats.probplot(yes, dist="norm", plot=ax[0])
ax[0].set_title("QQ-Plot: Churn=Yes")
stats.probplot(no, dist="norm", plot=ax[1])
ax[1].set_title("QQ-Plot: Churn=No")
plt.show()
(박스플롯)
Churn=Yes의 중앙값이 더 높음
분포가 오른쪽 꼬리를 가짐(고요금 고객 존재)
(QQ-Plot)
양쪽 끝에서 직선에서 벗어나는 모습 → 완벽한 정규성은 아님
하지만 큰 표본에서는 평균 비교 검정에 큰 영향 X
[결론]
정규성: 일부 위배, 하지만 표본이 크므로 t-검정 적용 가능
등분산성: 위배 → Welch t-test 사용
차이 해석: 고요금 고객에서 이탈률이 높을 가능성이 있음 → 가격 정책 검토 필요