1. T검정(t-test)
1.1 일표본 T-검정(One Sample t-test)
※ 정규성 가정
– 귀무가설(H0): 모평균의 값은 **이다
– 대립가설(H1): 모평균의 값은 **가 아니다
import pandas as pd
import scipy.stats as stats
from scipy.stats import shapiro
cats = pd.read_csv('data/cats_gpt.csv')
mu = 2.6
# 1. 정규성 검정
print(shapiro(cats['Bwt']))
# 2. (정규성을 만족하면) stats.ttest_1samp
print(stats.ttest_1samp(cats.Bwt, popmean=mu))
# 2. (정규성을 만족하지 않으면) Wil Coxon 검정
#stats.wilcoxon(cats.Bwt - mu, alternative='two-sided')

→ 데이터가 정규분포를 따라서 stats.ttest_1samp()을 수행
→ p-value는 유의수준 0.05 보다 크므로, “고양이의 몸무게가 2.6kg이다”라는 귀무가설을 채택
1.2 대응표본 T-검정(Paired Sample t-test)
※ 정규성 가정
– 귀무가설(H0): 두 모평균 사이의 차이는 없다
– 대립가설(H1): 두 모평균 사이의 차이가 있다
import pandas as pd
import scipy.stats as stats
from scipy.stats import shapiro
data = {'before': [7,3,4,5,2,1,6,6,5,4], 'after': [8,4,5,6,2,3,6,8,6,5]}
data = pd.DataFrame(data)
print(data)
# 1. 정규성 검정
print(shapiro(data)) # 정규분포 따름
# 2. (정규성을 만족하면) Paired t-test 검정
print(stats.ttest_rel(data['after'], data['before'], alternative='greater'))
print(data.mean())
# 2. (정규성을 만족하지 않으면) Wil Coxon 검정
#stats.wilcoxon(before, after)

→ p-value가 유의수준보다 작으므로, “수면영양제를 복용하기 전/후의 평균 수면 시간 차이는 통계적으로 유의하다
1.3 독립표본 T-검정(Independent Sample t-test)
※ 정규성, 등분산성 가정
→ 등분산성 만족하면 equal_var=True로, 만족하지 않으면 equal_var=False로 t-test 검정
– 귀무가설(H0): ** 전/후의 차이가 없다
– 대립가설(H1): ** 전/후의 차이가 있다
import pandas as pd
import scipy.stats as stats
cats = pd.read_csv('data/cats2_gpt.csv')
print(cats)
female = cats.loc[cats.Sex == 'F', 'Bwt']
male = cats.loc[cats.Sex == 'M', 'Bwt']
#1. (정규성을 만족하지 않으면) 만 휘트니 U 검정
#stats.mannwhitneyu(female, male)
# 2. (정규성을 만족하면) 등분산성 검정 - levene-test
print(stats.levene(female, male))
# 3. (등분산성을 만족하면, p-value가 0.05보다 크면) equal_var = True
print(stats.ttest_ind(female, male, equal_var=True))
print(female.mean())
print(male.mean())

2. 분산분석(ANOVA)
- 일원배치 분산분석: 독립변수 1개, 종속변수 1개
- 이원배치 분산분석: 독립변수 2개, 종속변수 1개
- 다원배치 분산분석: 독립변수 3개 이상, 종속변수 1개
- 다변량 분산분석(MANOVA): 독립변수 1개 이상, 종속변수 2개 이상
2.1 일원배치 분산분석(One-way ANOVA)
※ 정규성, 독립성, 등분산성 가정
– 귀무가설(H0): k개의 집단 간 모평균에는 차이가 없다.
– 대립가설(H1): k개의 집단 간 모평균이 모두 같다고 할 수 없다
→ p-value < 0.05인 경우, 사후검정을 통해 어떤 집단이 차이가 존재하는지 알아봐야 함

- 정규성을 만족하지 않으면, Kruskal-Wallis
stats.kruskal(setosa, versicolor, virginica)
- 등분산성을 만족하지 않으면, Welch ANOVA
import pingouin as pg
pg.welch_anova(data=df['value'], dv='sepal width', between='target')
혹은
from statsmodels.stats.oneway import anova_oneway
result = anova_oneway(df['value'], df['group'], use_var='unequal')
둘 다 만족하는 데이터로 ANOVA를 수행하자
import pandas as pd
import scipy.stats as stats
import seaborn as sns
import matplotlib.pyplot as plt
from statsmodels.stats.multicomp import MultiComparison
#from statsmodels.stats.multicomp import pairwise_tukeyhsd
Iris_data = pd.read_csv('data/iris_gpt.csv')
print(Iris_data['target'].unique())
print(Iris_data.target.value_counts())
target_list = Iris_data['target'].unique()
setosa = Iris_data[Iris_data['target'] == target_list[0]]['sepal width']
versicolor = Iris_data[Iris_data['target'] == target_list[1]]['sepal width']
virginica = Iris_data[Iris_data['target'] == target_list[2]]['sepal width']
print(target_list)
sns.scatterplot(x='target', y='sepal width', hue='target', style='target', s=100, data=Iris_data)
plt.show()
# 1. 정규성 검정 - 셋 다 만족해야 함
print(stats.shapiro(setosa))
print(stats.shapiro(versicolor))
print(stats.shapiro(virginica))
# 2. 등분산 검정
# 1을 만족하지 못했을 경우, kruskal 수행
stats.levene(setosa, versicolor, virginica)
# 3. One-way ANOVA
# 2를 만족하지 못했을 경우, pg.welch_anova(data=Iris_data, dv='sepal width', between='target')
stats.f_oneway(setosa, versicolor, virginica)
# 4. 사후검정
mc = MultiComparison(data=Iris_data['sepal width'], groups=Iris_data['target'])
tukeyhsd = mc.tukeyhsd(alpha=0.05)
#tukeyhsd = pairwise_tukeyhsd(endog=Iris_data['sepal width'], groups=Iris_data['target'], alpha=0.05)
fig = tukeyhsd.plot_simultaneous()
plt.show()
print(tukeyhsd.summary())

→ (Shapiro) p-value가 0.05보다 크므로 정규성 만족 – 하나라도 만족하지 않는다면 Kruskal-Wallis

→ (Levene) p-value가 0.05보다 크므로 등분산성 만족
→ (ANOVA) p-value가 유의수준 보다 작으므로 세 집단의 평균은 차이가 있음


→ (Tukeyhsd) 세 집단 모두 차이가 있음
2.2 이원배치 분산분석(Two-way ANOVA)
※ 정규성, 독립성, 등분산성 가정 + 교호작용 검증
– 귀무가설1(H0): a, b 변수의 상효작용 효과가 없다
– 귀무가설2(H0): a 변수에 따른 종속변수의 값에는 차이가 없다
– 귀무가설3(H0): b 변수에 따른 종속변수의 값에는 차이가 없다
– 대립가설1(H0): a, b 변수의 상효작용 효과가 있다
– 대립가설2(H0): a 변수에 따른 종속변수의 값에는 차이가 있다
– 대립가설3(H0): b 변수에 따른 종속변수의 값에는 차이가 있다

import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.formula.api import ols
from statsmodels.stats.anova import anova_lm
from statsmodels.graphics.factorplots import interaction_plot
mtcars = pd.read_csv('data/mtcars.csv')
mtcars = mtcars[['mpg', 'am', 'cyl']]
# 1. Two-way ANOVA 검정 수행
formula = 'mpg ~ C(cyl) + C(am) + C(cyl):C(am)'
#formula = 'mpg ~ C(cyl) * C(am)'
model = ols(formula, mtcars).fit()
aov_table = anova_lm(model, typ=2)
print(aov_table)
cyl = mtcars['cyl']
am = mtcars['am']
mpg = mtcars['mpg']
fig, ax = plt.subplots(figsize=(6,6))
fig = interaction_plot(cyl, am, mpg, colors=['red', 'blue'], markers=['D', '^'], ms=10, ax=ax) # x1, x2, y 순으로 작성
plt.show()


→ cyl 변수는 주효과 검정에서 귀무가설 기각
→ am 변수는 주효과 검정에서 귀무가설 채택
→ 교호작용 없음
from statsmodels.stats.multicomp import pairwise_tukeyhsd
# Tukey HSD 사후검정
# (cyl 그룹별 mpg 평균 차이)
tukey_result = pairwise_tukeyhsd(endog=mtcars['mpg'], groups=mtcars['cyl'], alpha=0.05)
print(tukey_result)
tukey_result.plot_simultaneous() # 시각화
plt.title('Tukey HSD: mpg ~ cyl')
plt.show()
# (am 그룹별 mpg 평균 차이)
tukey_result_am = pairwise_tukeyhsd(endog=mtcars['mpg'], groups=mtcars['am'], alpha=0.05)
print(tukey_result_am)
tukey_result_am.plot_simultaneous()
plt.title('Tukey HSD: mpg ~ am')
plt.show()
# 결합 그룹 생성
mtcars['group'] = mtcars['cyl'].astype(str) + '_' + mtcars['am'].astype(str)
# 결합 그룹 사후검정
tukey_result_inter = pairwise_tukeyhsd(endog=mtcars['mpg'], groups=mtcars['group'], alpha=0.05)
print(tukey_result_inter)
tukey_result_inter.plot_simultaneous()
plt.title('Tukey HSD: mpg ~ cyl:am interaction')
plt.show()



→ 모든 cyl 조합 간 연비 차이가 통계적으로 유의하다. (4 > 6 > 8) 기통 수가 많을수록 연비는 나빠지는 경향
3. 교차분석(카이제곱 검정)
→ 각 범주에 따른 결과 변수의 분포를 설명하거나, 두 변수가 상관이 있는지를 검정
→ 종속변수가 범주형
→ 각 범주의 기대빈도는 5 이상이어야 함
*적합성 검정
– 귀무가설: 타이타닉호의 생존자 중 남자 비율이 50%, 여자 비율이 50%이다
– 대립가설: 타이타닉호의 생존자 중 남자 비율이 50%, 여자 비율이 50%라고 할 수 없다
import pandas as pd
from scipy.stats import chisquare
df = pd.read_csv('data/titanic.csv')
df_t = df[df['Survived'] == 1]
table = df_t[['Sex']].value_counts()
print(table)
chi = chisquare(table, f_exp=[171,171]) # 비율 입력, np.array([342/2, 342/2]), np.array([100/3, 100/3, 100/3])
print('결과: ', chi)
observed = [40, 30, 30]
expected = [sum(observed) / 3] * 3

→ p-value가 유의수준 보다 작으므로, 귀무가설을 기각하여 대립가설을 채택한다
*독립성/동질성 검정
– 귀무가설: 두 변수는 독립이다
– 대립가설: 두 변수는 독립이 아니다
import pandas as pd
from scipy.stats import chi2_contingency
df = pd.read_csv('data/titanic.csv')
table = pd.crosstab(df['Pclass'], df['Survived'])
print(table)
chi, p, df, expect = chi2_contingency(table)
print('Statistic: ', chi)
print('p-value: ', p)
print('df: ', df)
print('expect: ', expect)

→ p-value가 유의수준 보다 작으므로, 귀무가설을 기각하여 독립이 아니라고 할 수 있음
동질성 검정의 귀무가설(H0): 변수1의 분포는 변수2에 관계없이 동일하다
*사후검정(혹시나 시험에 나올까봐) – 각 그룹 쌍마다 별도 교차표 작성 후 카이제곱 검정
import pandas as pd
import numpy as np
from itertools import combinations
from scipy.stats import chi2_contingency
from statsmodels.stats.multitest import multipletests
table = pd.DataFrame({
'성공': [30, 45, 25],
'실패': [20, 5, 25]
}, index=['A', 'B', 'C'])
chi2, p, dof, expected = chi2_contingency(table)
residuals = (table - expected) / np.sqrt(expected)
print("표준화 잔차 (z값):")
print(residuals)
group_labels = table.index.tolist()
results = []
for g1, g2 in combinations(group_labels, 2):
sub_table = table.loc[[g1, g2]]
chi2, p, *_ = chi2_contingency(sub_table)
results.append((f"{g1} vs {g2}", p))
# 다중 비교 문제(multiple comparisons) 발생 → p-value 보정
comparisons, raw_p = zip(*results)
adjusted = multipletests(raw_p, method='bonferroni')
for comp, pval, adj_p in zip(comparisons, raw_p, adjusted[1]):
print(f"{comp}: p = {pval:.4f}, Bonferroni 보정 p = {adj_p:.4f}")


→ A-B, B-C 그룹 간 비율 차이 있음
*피셔 검정 (Fisher’s Exact Test)
2×2 교차표, 5보다 작은 셀이 20% 넘는 경우
작은 표본에서 특히 유용하며, 카이제곱 검정을 사용할 수 없는 경우에 사용

import scipy.stats as stats
# 2x2 교차표 생성 (약물 A와 B에 대한 데이터)
# 행: 약물 A, B / 열: 치료 성공, 치료 실패
data = [[8, 2], # 약물 A
[1, 5]] # 약물 B
# Fisher's Exact Test 수행
oddsratio, p_value = stats.fisher_exact(data)
# 결과 출력
print(f"오즈비(odds ratio): {oddsratio:.4f}")
print(f"p-값: {p_value:.4f}")

※ 2×3 교차표에서도 적용 가능하지만, 계산 복잡성 때문에 통계 소프트웨어를 사용하여 수행하는 것이 일반적
'데이터 분석 > ADP 자격증 공부' 카테고리의 다른 글
| [ADP 필기] 군집분석 (0) | 2026.04.05 |
|---|---|
| [ADP 필기] 인공신경망 분석 (0) | 2026.04.05 |
| (파이썬 한권으로 끝내기) 머신러닝 분류 알고리즘 – 로지스틱 회귀, SVM, KNN, 의사결정나무, 앙상블/보팅, 나이브베이즈, 인공신경망 (0) | 2026.04.05 |
| [ADP 실기] Pandas, Numpy, 문자열 관련 함수 (1) | 2026.04.05 |
| [ADP 실기] 31회 복기 (1) | 2026.04.05 |