t검정 Python 구현 — SciPy로 일표본·독립표본(Student's·Welch's)·대응표본

이론은 이전 글로 다뤘으니, 이 글은 SciPy로 t검정 3종을 코드로 구현한다. 손으로 계산한 결과와 SciPy 결과가 일치하는지 확인하면서 가면 이해가 굳어진다.

평균치 검정 — t검정 (One / Two: Student's·Welch's / Paired)
두 집단 평균이 다른 게 진짜 차이인지 단순 변동성인지 — t검정의 당위성·가정(정규성·등분산성)·종류별 계산 예시.
taystudios.com/blog

1. Python 통계 라이브러리

Python 생태계의 대표 통계 라이브러리는 SciPyStatsmodels. (머신러닝 쪽은 scikit-learn.)

SciPy

  • p-value·기본 통계량을 빠르게 산출하는 데 최적화.
  • 대용량 데이터 처리·빠른 판단이 필요할 때.
  • 설치: pip install scipy

Statsmodels

  • 통계 모델 진단·평가에 필요한 다양한 기능 제공.
  • 각 변수의 검정통계량(t), 모델의 검정통계량(F), 결정계수 등 상세한 통계량 제공.
  • 설치: pip install statsmodels

이 글에선 SciPy로 t검정 3종을 구현한다.

2. One-sample t-test — stats.ttest_1samp

예시 — 약 복용 후 5명의 혈압이 평소 평균(120)과 다른지.

환자 투약 후 혈압 (mmHg)
환자1 118
환자2 121
환자3 119
환자4 117
환자5 120
from scipy import stats
import numpy as np

x1 = np.array([118, 121, 119, 117, 120])
mu0 = 120.0  # 비교할 모평균

result = stats.ttest_1samp(x1, popmean=mu0, alternative='two-sided', axis=0)
# TtestResult(statistic=-1.4142135623730951, pvalue=0.23019964108049873, df=4)

주요 인자

  • x1: 검정할 표본 (numpy array 권장)
  • popmean: 비교할 모평균
  • alternative: 'two-sided'(기본) / 'less'(좌측) / 'greater'(우측)
  • axis: 0(열, 기본) / 1(행) / None(평탄화)

axis 옵션 — 2D 배열일 때

arr = np.array([[1.5, 2.3, 3.7],
                [4.1, 5.2, 6.8],
                [7.4, 8.5, 9.1],
                [10.2, 11.4, 12.9],
                [13.3, 14.1, 15.6]])
mu0 = 5

# axis=0 — 열 기준 (각 열을 표본으로)
t_stat, p_val = stats.ttest_1samp(arr, popmean=mu0, axis=0)
# T-statistic: [1.0946 1.5652 2.1805]
# P-value:     [0.3352 0.1926 0.0947]

# axis=1 — 행 기준 (각 행을 표본으로)
t_stat, p_val = stats.ttest_1samp(arr, popmean=mu0, axis=1)
# T-statistic: [-3.8886  0.4678  6.6965  8.3224 13.8451]
# P-value:     [ 0.0602  0.6860  0.0216  0.0141  0.0052]

# axis=None — 평탄화 (전체를 한 표본으로)
t_stat, p_val = stats.ttest_1samp(arr, popmean=mu0, axis=None)
# T-statistic: 2.9475
# P-value:     0.0106

3. Two-sample t-test — stats.ttest_ind

예시 — 복용/미복용 두 집단의 혈압 비교.

환자 집단 투약 후 혈압
환자1 복용 115
환자2 복용 118
환자3 복용 116
환자4 미복용 122
환자5 미복용 124

3.1 Student's t-test — 등분산 가정 OK

from scipy import stats
import numpy as np

x1 = np.array([115, 118, 116])  # 복용
x2 = np.array([122, 124])       # 미복용

t_stat, p_val = stats.ttest_ind(x1, x2, equal_var=True, alternative='two-sided')
# T-statistic: -4.898979485566359
# P-value:      0.016276603459428517

주요 인자

  • x1, x2: 두 표본
  • equal_var:
  • True: 등분산성 가정 OK → Student's t-test
  • False: 등분산성 가정 X → Welch's t-test 자동 전환
  • alternative·axisttest_1samp 와 동일

axis — 2D 배열 비교

data  = np.array([[115, 118, 116],
                  [122, 124, 123]])
data1 = np.array([[231, 123, 132],
                  [223, 321, 421]])

# axis=0 — 열 기준
t_stat, p_val = stats.ttest_ind(data, data1, axis=0, equal_var=True)
# T-statistic: [-20.4136 -1.0197 -1.0862]
# P-value:     [  0.0024  0.4151  0.3909]

# axis=1 — 행 기준
t_stat, p_val = stats.ttest_ind(data, data1, axis=1, equal_var=True)
# T-statistic: [-1.3195 -3.4755]
# P-value:     [ 0.2575  0.0255]

3.2 Welch's t-test — 등분산 X

t_stat, p_val = stats.ttest_ind(x1, x2, equal_var=False, alternative='two-sided')
# T-statistic: -5.0000
# P-value:      0.02505

같은 메서드에서 equal_var=False 만 토글하면 Welch's. 등분산성 가정을 만족 못 할 때 쓴다.

4. Paired-samples t-test — stats.ttest_rel

예시 — 같은 환자의 투약 전/후 혈압. 짝지어진 데이터.

환자 투약 전 투약 후
환자1 130 120
환자2 128 119
환자3 135 125
환자4 132 123
환자5 129 121
import numpy as np
from scipy import stats

before = np.array([130, 128, 135, 132, 129])
after  = np.array([120, 119, 125, 123, 121])

t_stat, p_val = stats.ttest_rel(before, after, alternative='less')
# T-statistic: 24.58803425594304
# P-value:      0.9999918819424217

alternative='less' 는 "before < after" 를 대립가설로 본 것. 위 데이터는 before 가 더 크니 p 가 1에 가깝게 나온다. 약 복용 후 혈압이 낮아졌나 를 검정하고 싶다면 stats.ttest_rel(after, before, alternative='less') 로 — 이때 p 는 매우 작게 나온다.

주의: 양쪽 데이터의 쌍 개수가 일치해야 한다. 안 맞으면:

ValueError: unequal length arrays

결론

SciPy 의 세 함수면 평균치 t검정 3종 모두 한 줄이다.

  • 일표본 → stats.ttest_1samp(x, popmean)
  • 독립표본 → stats.ttest_ind(x1, x2, equal_var=True/False) — Student's vs Welch's
  • 대응표본 → stats.ttest_rel(before, after)

alternative (양측·단측) 와 axis (1D/2D) 만 챙기면 끝. 이론 글의 손계산 결과와 비교해보면 t·p 값이 일치한다.

참고


📦 이 글은 제가 운영하던 티스토리 블로그에서 옮겨온(migration) 글입니다. 원문: taehyuklee.tistory.com/26

이 글 공유𝕏f

댓글