[통계/실습] 세션 2회차(실습) - t-검정 : 단일표본 & 독립표본 & 대응표본

우선 라이브러리, 실습파일을 불러와보자! 결측치도 제거하고

 

신뢰구간 구하기

bmi_data = sample_df['bmi']

# 표본의 평균
sample_mean = np.mean(bmi_data)

# 표본의 표준편차
s = np.std(bmi_data)

# 표준오차의 근사값
se = s/np.sqrt(n)

# t 분포 기반 신뢰구간 구하기  (df:자유도)
stats.t.interval(confidence = 0.95, df = n-1, loc = sample_mean, scale=se)

신뢰구간!

 

가설검정 (t-검정)

 

1. 단일표본 t-검정: 표본이 1개일 때, 특정한 기준값과 비교

가설1
STEP1 : 가설 설정
STEP2 : 검정 방법 선택

#📌 정규성 검정 1. Q-Q plot

plt.figure(figsize=(4,4))
stats.probplot(bmi_data, plot=plt)
plt.title("Q-Q Plot")
plt.show()

➡️  정규성 만족

#📌 정규성 검정 2. Kolmogorov-Sminov

stat, p = stats.kstest(bmi_data, 'norm', args=(sample_mean, s))
print("statistics=", stat, "p-value", p)

if p > 0.05:
    print("정규성을 만족한다고 볼 수 있음(귀무가설 기각 불가)")
else:
    print("정규성을 만족하지 않는다(귀무가설 기각)")
    
>>> 출력:
        statistics= 0.09664739454966476 p-value 0.9560303107107065
        정규성을 만족한다고 볼 수 있음(귀무가설 기각 불가)
🤔 그래서 이거를 왜 하는데?

단일표본 t검정이라는 것을 할건데, 그거는 1개의 표본을 갖고있을 때, 그 평균이 우리가 생각한 평균과 같냐? 같지않냐?를 확인하는 거야!  t-검정이라는 것이 정규성을 만족해야 해서 정규성 검정을 하고 있다. 표본의 크기가 30이 안되니, 정규성을 꼭 봐야한다! 정규성 검정을 해서 t검정을 써도 되는지 아닌지를 결정할 수 있다!!

정규성 검정을 볼 수 있는게 1) Q-Q plot : 대각선에 맞으면 정규성 만족! (혹시 모르니 한번 더 검정) 2) ks 검정 도 해본다. 
ks 검정은 무엇을 하는거냐? 지금의 데이터가 정규분포를 만족하는지를 확인하는 검정이다...!

📌 p-value가 유의수준(0.05)보다 높다. 귀무가설을 기각할 수 없다(채택한다). 정규성을 만족한다. 여기서는 ✅ !
📌 p-value가 유의수준(0.05)보다 낮다. 귀무가설을 기각한다. 정규성을 만족하지 않는다고 결론을 내린다.

➡️ 우리는 단일표본 t검정을 해도된다!를 확인한 것임!!!

🤔 왜일까? 귀무가설 기각 불가 → 정규성 만족 (GPT)

더보기

Q: p-value가 유의수준(0.05)보다 작은 경우 정규성을 만족하지 않음(귀무가설 기각)과 유의수준보다 큰 경우 정규성을 만족한다(귀무가설 기각 불가)라고 되어 있는데, 이거는 기본값이야? 왜 그렇게 정해진거야?


A:

1. KS 검정의 귀무가설(H₀)과 대립가설(H₁)

  • 귀무가설(H₀) : 데이터는 특정 분포(여기서는 정규분포)를 따른다.
  • 대립가설(H₁) : 데이터는 그 분포를 따르지 않는다.

즉, KS 검정은 "데이터가 정규분포인지 아닌지"를 확인하는 절차예요.

2. p-value의 의미 ⭐️⭐️⭐️⭐️⭐️

p-value는 귀무가설이 참일 때, 현재와 같거나 더 극단적인 데이터가 나올 확률이에요.

  • p가 아주 작다면 → "정규분포라고 가정했는데, 이런 데이터가 나올 가능성이 거의 없음"
    → 따라서 귀무가설을 기각.
  • p가 크다면 → "정규분포라고 가정해도, 이런 데이터가 충분히 나올 수 있음"
    → 따라서 귀무가설을 기각할 근거가 없음.

3. 유의수준(α)의 역할

  • 유의수준(보통 0.05)은 연구자가 정하는 기준값입니다.
  • "귀무가설이 맞는데도, 잘못 기각할 최대 허용 확률"을 의미해요 (제1종 오류).
  • 0.05가 "기본값"처럼 자주 쓰이는 건 관습적인 이유예요. 통계학적으로 꼭 0.05여야 하는 법은 없고, 0.01, 0.10 등을 쓰기도 합니다. (예: 의학 연구는 보수적으로 0.01, 탐색적 연구는 0.10)

4. 정리

  • p < α → 귀무가설 기각 → 정규성을 만족하지 않는다.
  • p ≥ α → 귀무가설 채택이 아니라 "기각할 수 없음" → 정규성을 만족한다고 볼 있다.

즉, "p ≥ 0.05 → 정규성을 만족한다"라는 표현은 사실 "정규성을 기각할 근거가 없다"라는 뜻이에요. 정규성을 완전히 증명하는 건 아니고, 반박할 증거가 부족하다는 쪽이 더 정확합니다.

👉 결론:

"p-value 유의수준보다 작으면 정규성 기각, 크면 정규성 유지"라는 원칙은 KS 검정만의 기본값이 아니라, 모든 가설검정에 공통적인 규칙이에요. 0.05 전통적으로 많이 쓰이는 관습적인 기준일 , 연구 상황에 맞게 바꿀 수도 있습니다.

정규성 만족, STEP3 ❘ STEP4

# 단일 표본 t-검정 : stats.ttest_1samp

t_stat, p_value = stats.ttest_1samp(bmi_data, popmean = 28)
print("단일 표본 t-검정")
print(f"t_stat(검정통계량) = {t_stat:.4f}, p-value = {p_value:.4f}")

>>> 출력:
        단일 표본 t-검정
        t_stat(검정통계량) =1.7641, p_value = 0.0904

STEP5 : 귀무가설 기각 여부 결정


가설2
STEP1 - STEP4

# 단일 표본 t-검정 : stats.ttest_1samp
t_stat, p_value = stats.ttest_1samp(bmi_data, popmean=30, alternative="greater")
print("단일표본 t-검정(단측검정)")
print(f"t_stat ={t_stat:.4f}, p_value = {p_value:.4f}")

>>> 출력:
        단일표본 t-검정(단측검정)
        t_stat(검정 통계량) =0.2112, p_value = 0.4173

STEP5 : 기각 여부 결정


2. 독립 표본 t-검정(이표본 t-검정) : 서로 다른 표본 2개 평균 비교

# 뇌졸중에 걸린 사람: A
sample_df_a = df.loc[df['stroke']==1].sample(n=40, random_state=1)

# 뇌졸중에 걸리지 않은 사람: B
sample_df_b = df.loc[df['stroke']==0].sample(n=40, random_state=1)
bmi_data_a = sample_df_a['bmi'].values
bmi_data_b = sample_df_b['bmi'].values

STEP1 : 가설설정

# 두 집단의 평균 구하기
np.mean(bmi_data_a), np.mean(bmi_data_b)
➡️ (np.float64(30.115), np.float64(28.354999999999997))
어라? 평균이 다른거 아니야? 라고 생각할 수 있겠지만, 우리에게 중요한 건 이 차이가 통계적으로 유의미한지 이다.
우연히 발생한 정도인지, 아니면 항상 차이가 나타는는 건지 검증이 필요하다. 이 말이다!

2-1. A, B에 대한 정규성 검정 : Q-Q plot 

# 정규성 검정 1-A) Q-Q plot : bmi_data_a
plt.figure(figsize=(4,4))
stats.probplot(bmi_data_a, plot=plt)
plt.title("Q-Q Plot")
plt.show()

# 정규성 검정 1-B) Q-Q plot : bmi_data_b
plt.figure(figsize=(4,4))
stats.probplot(bmi_data_b, plot=plt)
plt.title("Q-Q Plot")
plt.show()

2-2. A, B에 대한 정규성 검정 : ks 검정

# 정규성 검정 2-A) Kolmogorov-Sminov
stat, p = stats.kstest(bmi_data_a, 'norm', args=(sample_mean, s))
print("statistics=", stat, "p-value", p)

if p > 0.05:
    print("정규성을 만족한다고 볼 수 있음(귀무가설 기각 불가)")
else:
    print("정규성을 만족하지 않는다(귀무가설 기각)")

>>> 출력:
        statistics= 0.14779603744458747, p-value 0.3148685028513951
        정규성을 만족한다고 볼 수 있음(귀무가설 기각 불가)
# 정규성 검정 2-B) Kolmogorov-Sminov
stat, p = stats.kstest(bmi_data_b, 'norm', args=(sample_mean, s))
print("statistics=", stat, "p-value", p)

if p > 0.05:
    print("정규성을 만족한다고 볼 수 있음(귀무가설 기각 불가)")
else:
    print("정규성을 만족하지 않는다(귀무가설 기각)")

>>> 출력:
        statistics= 0.17988614319046958, p-value 0.13253536968265345
        정규성을 만족한다고 볼 수 있음(귀무가설 기각 불가)

2-3. 등분산성 검정 : Levene 검정

# 등분산성 검정 : Levene 검정
stat, p = stats.levene(bmi_data_a, bmi_data_b)
print('Levene Test : stat =', stat, "p-value=", p)

if p > 0.05:
    print("등분산 가정 만족 (일반 t-test 사용 가능)")
else:
    print("등분산 가정 불만족 (Welch t-test 권장)")
➡️ Levene Test : stat = 5.643653761236718,   p-value= 0.019973459389287927
     등분산 가정 불만족 (Welch t-test 권장)

엇 이런...등분산성 가정이 깨져버렸다. 즉, 두 집단의 분산이 다르다. 따라서 이 경우에는 일반적은 t-test가 아닌 Welch's t-test를 해야한다.

STEP3 ❘ STEP4

# Welch's t-test
# equal_var : 분산이 같은지? 달라서 False! True이면 일반 독립표본 t-검정

t_stat, p_value = stats.ttest_ind(bmi_data_a, bmi_data_b, equal_var=False)
print("Two-sample Welch's t-test")
print(f"t-statistics = {t_stat:.4f}, p-value ={p_value:.4f}")
➡️ Two-sample Welch's t-test
     t-statistics = 1.3496,  p-value =0.1815
p_value가 유의수준 0.05보다 높다 → 귀무가설 채택(기각 못함)  두 집단의 bmi 평균이 같다.
(
통계적으로 두 집단의 bmi 평균이 같다. 차이는 실제 조금 있는데 그게 통계적으로 유의한 차이는 아니다.)

3. 대응 표본 t-검정 : 같은 집단의 전/후 비교

transaction_df 파일에서 실습

파일 불러오기

# 거래일자(t_dat) 기준으로 분리
before_df = transaction_df.loc[transaction_df['t_dat']<'2019-04-30']
after_df = transaction_df.loc[transaction_df['t_dat']>='2019-04-30']

# '2019-04-30' 이전과 이후 모두 구매한 사람만 남기기 (교집합 구하기!)
customer_id_lst = np.intersect1d(before_df['customer_id'], after_df['customer_id']

before_df_sample = before_df.loc[before_df['customer_id'].isin(customer_id_lst)]
after_df_sample = after_df.loc[after_df['customer_id'].isin(customer_id_lst)]

# 곡 고객별로 평균 구매금액 구하기
before_df_sample_price = before_df_sample.groupby('customer_id')['price'].mean()
after_df_sample_price = after_df_sample.groupby('customer_id')['price'].mean()

price_sample_before = before_df_sample_price.values
price_sample_after = after_df_sample_price.values

3-1. 양측 검정( 같다 vs 다르다 )

np.mean(price_sample_before), np.mean(price_sample_after)
출력 >>> (np.float64(0.028274348830291396), np.float64(0.028059526241708318))

1) 정규성 검정 : Q-Q plot (before vs after)

# 1. Q-Q plot 시각화
fig, ax = plt.subplots(1,2, figsize=(8,4))

stats.probplot(price_sample_before, plot=ax[0])
ax[0].set_title("QQ plot(A)")

stats.probplot(price_sample_after, plot= ax[1])
ax[1].set_title("QQ plot(B)")
plt.show()

2) 정규성 검정 : ks 검정

# before : KS 검정
sample_mean = np.mean(price_sample_before)
s = np.std(price_sample_before)
stat, p =stats.kstest(price_sample_before,'norm',args=(sample_mean,s))
print("statistics =",stat, "p-value", p)
if p >0.05:
    print("정규성을 만족한다고 볼 수 있음(귀무가설 기각 불가)")
else:
    print("정규성을 만족하지 않음(귀무가설 기각)")
출력 >>> 
statistics = 0.13694736300021104,  p-value 0.0
정규성을 만족하지 않음(귀무가설 기각)
# after : KS 검정
sample_mean = np.mean(price_sample_after)
s = np.std(price_sample_after)
stat, p =stats.kstest(price_sample_after,'norm',args=(sample_mean,s))
print("statistics =",stat, "p-value", p)
if p >0.05:
    print("정규성을 만족한다고 볼 수 있음(귀무가설 기각 불가)")
else:
    print("정규성을 만족하지 않음(귀무가설 기각)")
출력 >>>
statistics = 0.12770389442404773 p-value 0.0
정규성을 만족하지 않음(귀무가설 기각)
# 대응표본 t-test : stats.ttest_rel
t_stat , p_value = stats.ttest_rel(price_sample_before, price_sample_after)
print("대응표본 t-검정")
print("statistics =",t_stat, "p_value", p)
출력 >>>
대응표본 t-검정
statistics = 3.339095659130551,  p_value 0.0

 

3-2. 단측 검정 (크다 vs 작거나 같다)

# 대응표본 t-test : ttest_rel , alternative = 'greater'
t_stat, p_value = stats.ttest_rel(price_sample_before, price_sample_after, alternative='greater')
print("statistics =",t_stat, "p_value", p)
출력 >>>
statistics = 3.339095659130551,  p_value 0.0
💡 p-value가 매우 작으므로, 귀무가설 기각 > 대립가설 채택
         : 2019-04-30 이전의 평균 구매금액이 이후의 평균 구매금액보
크다!