t검정 Python 구현 — SciPy로 일표본·독립표본(Student's·Welch's)·대응표본
이론은 이전 글로 다뤘으니, 이 글은 SciPy로 t검정 3종을 코드로 구현한다. 손으로 계산한 결과와 SciPy 결과가 일치하는지 확인하면서 가면 이해가 굳어진다.
1. Python 통계 라이브러리
Python 생태계의 대표 통계 라이브러리는 SciPy 와 Statsmodels. (머신러닝 쪽은 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-testFalse: 등분산성 가정 X → Welch's t-test 자동 전환alternative·axis는ttest_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

댓글