๊ฐ์ค๊ฒ์ (Hypothesis Testing)
0. ์ฌ์ ์ค๋น (Setup)
๋ค์ํ ๋ฐ์ดํฐ์ ์ ํ์ฉํ์ฌ ๊ฐ์ค๊ฒ์ ์ ์ค์ตํฉ๋๋ค.
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from scipy import stats
from scipy.stats import (
ttest_ind, ttest_rel, ttest_1samp,
mannwhitneyu, wilcoxon, kruskal,
chi2_contingency, fisher_exact,
f_oneway, shapiro, levene, bartlett,
spearmanr, pearsonr, kendalltau,
kstest, normaltest, anderson
)
import warnings
warnings.filterwarnings('ignore')
# 1. Titanic Dataset
titanic = sns.load_dataset('titanic')
print(f"Titanic: {titanic.shape}")
# 2. Iris Dataset
iris = sns.load_dataset('iris')
print(f"Iris: {iris.shape}")
# 3. Tips Dataset
tips = sns.load_dataset('tips')
print(f"Tips: {tips.shape}")
# 4. Diamonds Dataset (์ํ๋ง)
diamonds = sns.load_dataset('diamonds').sample(n=1000, random_state=42)
print(f"Diamonds: {diamonds.shape}")Titanic: (891, 15) Iris: (150, 5) Tips: (244, 7) Diamonds: (1000, 10)
1. ๊ฐ์ค๊ฒ์ ์ ๊ธฐ์ด
ํต์ฌ ๊ฐ๋
| ์ฉ์ด | ์ค๋ช |
|---|---|
| ๊ท๋ฌด๊ฐ์ค (Hโ) | โ์ฐจ์ด/ํจ๊ณผ๊ฐ ์๋คโ - ๊ธฐ๋ณธ ๊ฐ์ |
| ๋๋ฆฝ๊ฐ์ค (Hโ) | โ์ฐจ์ด/ํจ๊ณผ๊ฐ ์๋คโ - ์ฆ๋ช ํ๊ณ ์ถ์ ๊ฒ |
| p-value | ๊ท๋ฌด๊ฐ์ค์ด ์ฐธ์ผ ๋, ํ์ฌ ๊ฒฐ๊ณผ๊ฐ ๋์ฌ ํ๋ฅ |
| ์ ์์์ค (ฮฑ) | ๋ณดํต 0.05 (5%) ์ฌ์ฉ |
| ๊ฒ์ ๋ ฅ (Power) | ์ค์ ํจ๊ณผ๊ฐ ์์ ๋ ์ด๋ฅผ ํ์งํ ํ๋ฅ |
๊ฒ์ ์ ํ ๊ฐ์ด๋
๋ฐ์ดํฐ ์ ํ?
โโโ ์ฐ์ํ (Continuous)
โ โโโ ์ ๊ท๋ถํฌ โ ๋ชจ์์ ๊ฒ์ (t-test, ANOVA)
โ โโโ ๋น์ ๊ท๋ถํฌ โ ๋น๋ชจ์์ ๊ฒ์ (Mann-Whitney, Kruskal-Wallis)
โ
โโโ ๋ฒ์ฃผํ (Categorical)
โโโ 2ร2 ํ (์ํ๋ณธ) โ Fisher's Exact Test
โโโ ๊ทธ ์ธ โ Chi-Square Test2. ์ ๊ท์ฑ ๊ฒ์ (Normality Tests)
๐ฏ ์ธ์ ์ฌ์ฉํ๋์?
t-๊ฒ์ , ANOVA ๊ฐ์ ๋ชจ์์ ๊ฒ์ ์ ์ํํ๊ธฐ ์ ์ ๋ฐ์ดํฐ๊ฐ ์ ๊ท๋ถํฌ๋ฅผ ๋ฐ๋ฅด๋์ง ํ์ธํ ๋ ์ฌ์ฉํฉ๋๋ค. ์ ๊ท์ฑ์ด ๋ง์กฑ๋์ง ์์ผ๋ฉด ๋น๋ชจ์์ ๊ฒ์ (Mann-Whitney, Kruskal-Wallis ๋ฑ)์ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
Shapiro-Wilk Test
๐ ์ฌ์ฉ ์ํฉ ์์
- ์ ์ฝ ์์์ํ์์ ํ์ ์ธก์ ๊ฐ์ด ์ ๊ท๋ถํฌ๋ฅผ ๋ฐ๋ฅด๋์ง ํ์ธ
- A/B ํ ์คํธ ์ , ์ฌ์ฉ์ ์ฒด๋ฅ ์๊ฐ ๋ฐ์ดํฐ์ ๋ถํฌ ํ์ธ
- ์ ์กฐ ๊ณต์ ์์ ์ ํ ๋ฌด๊ฒ ๋ฐ์ดํฐ๊ฐ ์ ์ ๋ถํฌ์ธ์ง ํ์ง ๊ฒ์ฌ
๐ก ํน์ง: ๊ฐ์ฅ ๊ฒ์ ๋ ฅ์ด ๋์ ์ ๊ท์ฑ ๊ฒ์ . ๋จ, n < 5000์ผ ๋ ์ฌ์ฉ ๊ถ์ฅ.
# Titanic: ๋์ด ๋ถํฌ์ ์ ๊ท์ฑ ๊ฒ์
ages = titanic['age'].dropna()
stat, p_value = shapiro(ages)
print("=== Shapiro-Wilk ์ ๊ท์ฑ ๊ฒ์ ===")
print(f"๋ฐ์ดํฐ: Titanic ์น๊ฐ ๋์ด (n={len(ages)})")
print(f"ํต๊ณ๋: {stat:.4f}")
print(f"p-value: {p_value:.4f}")
print(f"๊ฒฐ๋ก : {'์ ๊ท๋ถํฌ๋ฅผ ๋ฐ๋ฆ โ' if p_value >= 0.05 else '์ ๊ท๋ถํฌ๋ฅผ ๋ฐ๋ฅด์ง ์์ โ'}")
# ์๊ฐํ
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
axes[0].hist(ages, bins=30, edgecolor='black', alpha=0.7)
axes[0].set_title('Age Distribution')
axes[0].set_xlabel('Age')
stats.probplot(ages, dist="norm", plot=axes[1])
axes[1].set_title('Q-Q Plot')
plt.tight_layout()
plt.show()=== Shapiro-Wilk ์ ๊ท์ฑ ๊ฒ์ === ๋ฐ์ดํฐ: Titanic ์น๊ฐ ๋์ด (n=714) ํต๊ณ๋: 0.9816 p-value: 0.0000 ๊ฒฐ๋ก : ์ ๊ท๋ถํฌ๋ฅผ ๋ฐ๋ฅด์ง ์์ โ
DโAgostino-Pearson Test
๐ ์ฌ์ฉ ์ํฉ ์์
- ๊ธ์ต ๋ฐ์ดํฐ์ ์์ต๋ฅ ๋ถํฌ๊ฐ ์ ๊ท๋ถํฌ์ธ์ง ํ์ธ (์๋/์ฒจ๋๊ฐ ์ค์ํ ๊ฒฝ์ฐ)
- ์ค๋ฌธ์กฐ์ฌ ์ ์์ ๋ถํฌ ํํ ๊ฒ์
- ์ํ ์ ์ ๋ถํฌ์ ๋น๋์นญ์ฑ ํ์ธ
๐ก ํน์ง: **์๋(skewness)์ ์ฒจ๋(kurtosis)**๋ฅผ ํจ๊ป ๊ณ ๋ ค. ๋ถํฌ์ ํํ๊ฐ ์ค์ํ ๋ ์ ์ฉ. n โฅ 20์ผ ๋ ์ฌ์ฉ ๊ฐ๋ฅ.
# Iris: ๊ฝ์ ๊ธธ์ด์ ์ ๊ท์ฑ ๊ฒ์
petal_length = iris['petal_length']
stat, p_value = normaltest(petal_length)
print("=== D'Agostino-Pearson ์ ๊ท์ฑ ๊ฒ์ ===")
print(f"๋ฐ์ดํฐ: Iris ๊ฝ์ ๊ธธ์ด (n={len(petal_length)})")
print(f"ํต๊ณ๋: {stat:.4f}")
print(f"p-value: {p_value:.4f}")
print(f"๊ฒฐ๋ก : {'์ ๊ท๋ถํฌ๋ฅผ ๋ฐ๋ฆ โ' if p_value >= 0.05 else '์ ๊ท๋ถํฌ๋ฅผ ๋ฐ๋ฅด์ง ์์ โ'}")=== D'Agostino-Pearson ์ ๊ท์ฑ ๊ฒ์ === ๋ฐ์ดํฐ: Iris ๊ฝ์ ๊ธธ์ด (n=150) ํต๊ณ๋: 31.5324 p-value: 0.0000 ๊ฒฐ๋ก : ์ ๊ท๋ถํฌ๋ฅผ ๋ฐ๋ฅด์ง ์์ โ
Kolmogorov-Smirnov Test
๐ ์ฌ์ฉ ์ํฉ ์์
- ๋์ฉ๋ ๋ฐ์ดํฐ(n > 5000)์ ์ ๊ท์ฑ ๊ฒ์
- ์ ๊ท๋ถํฌ ์ธ์ ๋ค๋ฅธ ์ด๋ก ์ ๋ถํฌ(์ง์๋ถํฌ, ๊ท ๋ฑ๋ถํฌ ๋ฑ)์ ๋น๊ต
- ๋ ํ๋ณธ์ ๋ถํฌ๊ฐ ๋์ผํ์ง ๋น๊ต (2-sample KS test)
๐ก ํน์ง: ํ๋ณธ ํฌ๊ธฐ์ ๋ ๋ฏผ๊ฐํ์ฌ ๋์ฉ๋ ๋ฐ์ดํฐ์ ์ ํฉ. ๋ค์ํ ๋ถํฌ์ ๋น๊ต ๊ฐ๋ฅ.
# Tips: ํ ๊ธ์ก์ ์ ๊ท์ฑ ๊ฒ์
tip_values = tips['tip']
# ํ์คํ ํ ๊ฒ์
tip_standardized = (tip_values - tip_values.mean()) / tip_values.std()
stat, p_value = kstest(tip_standardized, 'norm')
print("=== Kolmogorov-Smirnov ์ ๊ท์ฑ ๊ฒ์ ===")
print(f"๋ฐ์ดํฐ: Tips ํ ๊ธ์ก (n={len(tip_values)})")
print(f"ํต๊ณ๋: {stat:.4f}")
print(f"p-value: {p_value:.4f}")
print(f"๊ฒฐ๋ก : {'์ ๊ท๋ถํฌ๋ฅผ ๋ฐ๋ฆ โ' if p_value >= 0.05 else '์ ๊ท๋ถํฌ๋ฅผ ๋ฐ๋ฅด์ง ์์ โ'}")=== Kolmogorov-Smirnov ์ ๊ท์ฑ ๊ฒ์ === ๋ฐ์ดํฐ: Tips ํ ๊ธ์ก (n=244) ํต๊ณ๋: 0.0975 p-value: 0.0186 ๊ฒฐ๋ก : ์ ๊ท๋ถํฌ๋ฅผ ๋ฐ๋ฅด์ง ์์ โ
Anderson-Darling Test
๐ ์ฌ์ฉ ์ํฉ ์์
- ๊ทน๋จ๊ฐ(์ด์์น)์ด ์ค์ํ ๋ถ์์์ ์ ๊ท์ฑ ํ์ธ (์ํ ๊ด๋ฆฌ, ๋ณดํ ๋ฑ)
- ๋ถํฌ์ ๊ผฌ๋ฆฌ ๋ถ๋ถ์ด ์ ๊ท๋ถํฌ์ ์ผ๋ง๋ ๋ค๋ฅธ์ง ํ์ธ
- ์ฌ๋ฌ ์ ์์์ค์์ ๋์์ ํ๋จ์ด ํ์ํ ๋
๐ก ํน์ง: ๋ถํฌ์ ๊ผฌ๋ฆฌ(tail) ๋ถ๋ถ์ ๋ ๋ฏผ๊ฐ. ๊ทน๋จ๊ฐ์ด ์ค์ํ ๊ธ์ต/๋ณดํ ๋ถ์ผ์์ ์ ํธ.
# Diamonds: ๊ฐ๊ฒฉ์ ์ ๊ท์ฑ ๊ฒ์
prices = diamonds['price']
result = anderson(prices, dist='norm')
print("=== Anderson-Darling ์ ๊ท์ฑ ๊ฒ์ ===")
print(f"๋ฐ์ดํฐ: Diamonds ๊ฐ๊ฒฉ (n={len(prices)})")
print(f"ํต๊ณ๋: {result.statistic:.4f}")
print("\n์ ์์์ค๋ณ ์๊ณ๊ฐ:")
for i, (cv, sl) in enumerate(zip(result.critical_values, result.significance_level)):
result_str = "๊ธฐ๊ฐ" if result.statistic > cv else "์ฑํ"
print(f" {sl}%: ์๊ณ๊ฐ = {cv:.4f} โ Hโ {result_str}")=== Anderson-Darling ์ ๊ท์ฑ ๊ฒ์ === ๋ฐ์ดํฐ: Diamonds ๊ฐ๊ฒฉ (n=1000) ํต๊ณ๋: 47.8932 ์ ์์์ค๋ณ ์๊ณ๊ฐ: 15.0%: ์๊ณ๊ฐ = 0.5740 โ Hโ ๊ธฐ๊ฐ 10.0%: ์๊ณ๊ฐ = 0.6540 โ Hโ ๊ธฐ๊ฐ 5.0%: ์๊ณ๊ฐ = 0.7850 โ Hโ ๊ธฐ๊ฐ 2.5%: ์๊ณ๊ฐ = 0.9150 โ Hโ ๊ธฐ๊ฐ 1.0%: ์๊ณ๊ฐ = 1.0890 โ Hโ ๊ธฐ๊ฐ
3. ๋ฑ๋ถ์ฐ ๊ฒ์ (Homogeneity of Variance)
๐ฏ ์ธ์ ์ฌ์ฉํ๋์?
๋ ๋ฆฝํ๋ณธ t-๊ฒ์ ์ด๋ ANOVA๋ฅผ ์ํํ๊ธฐ ์ , ๋น๊ตํ๋ ค๋ ๊ทธ๋ฃน๋ค์ ๋ถ์ฐ์ด ๋์ผํ์ง ํ์ธํ ๋ ์ฌ์ฉํฉ๋๋ค. ๋ฑ๋ถ์ฐ ๊ฐ์ ์ด ์๋ฐฐ๋๋ฉด Welchโs t-test๋ Games-Howell ์ฌํ๊ฒ์ ์ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
Leveneโs Test
๐ ์ฌ์ฉ ์ํฉ ์์
- A/B ํ ์คํธ์์ ์คํ๊ตฐ๊ณผ ๋์กฐ๊ตฐ์ ๊ตฌ๋งค๊ธ์ก ๋ถ์ฐ์ด ๊ฐ์์ง ํ์ธ
- ๋จ๋ ๊ทธ๋ฃน์ ์ํ ์ ์ ๋ถ์ฐ์ด ๋์ผํ์ง ๊ฒ์
- ์ฌ๋ฌ ๊ณต์ฅ์์ ์์ฐ๋ ์ ํ ํ์ง์ ์ฐํฌ๋๊ฐ ๋์ผํ์ง ํ์ธ
๐ก ํน์ง: ์ ๊ท์ฑ ๊ฐ์ ์ด ํ์ ์์ด ๋ก๋ฒ์คํธํจ. ๋น์ ๊ท ๋ฐ์ดํฐ์์๋ ์ฌ์ฉ ๊ฐ๋ฅ.
# Titanic: ์์กด ์ฌ๋ถ์ ๋ฐ๋ฅธ ๋์ด ๋ถ์ฐ ๋น๊ต
survived_ages = titanic[titanic['survived'] == 1]['age'].dropna()
died_ages = titanic[titanic['survived'] == 0]['age'].dropna()
stat, p_value = levene(survived_ages, died_ages)
print("=== Levene's ๋ฑ๋ถ์ฐ ๊ฒ์ ===")
print(f"์์กด์ ๋์ด ๋ถ์ฐ: {survived_ages.var():.2f}")
print(f"์ฌ๋ง์ ๋์ด ๋ถ์ฐ: {died_ages.var():.2f}")
print(f"ํต๊ณ๋: {stat:.4f}")
print(f"p-value: {p_value:.4f}")
print(f"๊ฒฐ๋ก : {'๋ฑ๋ถ์ฐ ๊ฐ์ ์ถฉ์กฑ โ' if p_value >= 0.05 else '๋ฑ๋ถ์ฐ ๊ฐ์ ๋ถ์ถฉ์กฑ โ'}")=== Levene's ๋ฑ๋ถ์ฐ ๊ฒ์ === ์์กด์ ๋์ด ๋ถ์ฐ: 207.03 ์ฌ๋ง์ ๋์ด ๋ถ์ฐ: 199.41 ํต๊ณ๋: 0.1557 p-value: 0.6933 ๊ฒฐ๋ก : ๋ฑ๋ถ์ฐ ๊ฐ์ ์ถฉ์กฑ โ
Bartlettโs Test
๐ ์ฌ์ฉ ์ํฉ ์์
- ์ ๊ท๋ถํฌ๊ฐ ํ์ธ๋ ๋ฐ์ดํฐ์์ ๊ทธ๋ฃน ๊ฐ ๋ถ์ฐ ๋น๊ต
- ์์์ํ์์ ์ฌ๋ฌ ์ฉ๋ ๊ทธ๋ฃน์ ๋ฐ์ ๋ณ๋์ฑ ๋น๊ต
- ํ์ง ๊ด๋ฆฌ์์ ์ฌ๋ฌ ์์ฐ ๋ผ์ธ์ ๋ถ์ฐ ๋์ผ์ฑ ๊ฒ์
๐ก ํน์ง: ์ ๊ท๋ถํฌ๋ฅผ ๋ฐ๋ฅผ ๋ ๊ฐ์ฅ ๊ฐ๋ ฅํ ๋ฑ๋ถ์ฐ ๊ฒ์ . ์ ๊ท์ฑ์ด ์๋ฐฐ๋๋ฉด Levene ์ฌ์ฉ ๊ถ์ฅ.
# Iris: ํ์ข
๋ณ ๊ฝ๋ฐ์นจ ๊ธธ์ด ๋ถ์ฐ ๋น๊ต
setosa = iris[iris['species'] == 'setosa']['sepal_length']
versicolor = iris[iris['species'] == 'versicolor']['sepal_length']
virginica = iris[iris['species'] == 'virginica']['sepal_length']
stat, p_value = bartlett(setosa, versicolor, virginica)
print("=== Bartlett's ๋ฑ๋ถ์ฐ ๊ฒ์ ===")
print(f"Setosa ๋ถ์ฐ: {setosa.var():.4f}")
print(f"Versicolor ๋ถ์ฐ: {versicolor.var():.4f}")
print(f"Virginica ๋ถ์ฐ: {virginica.var():.4f}")
print(f"ํต๊ณ๋: {stat:.4f}")
print(f"p-value: {p_value:.4f}")
print(f"๊ฒฐ๋ก : {'๋ฑ๋ถ์ฐ ๊ฐ์ ์ถฉ์กฑ โ' if p_value >= 0.05 else '๋ฑ๋ถ์ฐ ๊ฐ์ ๋ถ์ถฉ์กฑ โ'}")=== Bartlett's ๋ฑ๋ถ์ฐ ๊ฒ์ === Setosa ๋ถ์ฐ: 0.1242 Versicolor ๋ถ์ฐ: 0.2664 Virginica ๋ถ์ฐ: 0.4043 ํต๊ณ๋: 16.0057 p-value: 0.0003 ๊ฒฐ๋ก : ๋ฑ๋ถ์ฐ ๊ฐ์ ๋ถ์ถฉ์กฑ โ
4. T-๊ฒ์ (T-Tests)
๋จ์ผํ๋ณธ t-๊ฒ์ (One-Sample T-Test)
๐ ์ฌ์ฉ ์ํฉ ์์
- ํ์ง ๊ฒ์ฌ: ๊ณต์ฅ์์ ์์ฐ๋ ๋ฐฐํฐ๋ฆฌ ํ๊ท ์๋ช ์ด ๊ณต์นญ ์๋ช 1000์๊ฐ๊ณผ ๊ฐ์์ง ๊ฒ์ฆ
- ๋ง์ผํ : ๊ณ ๊ฐ ํ๊ท ๋ง์กฑ๋๊ฐ ๋ชฉํ์น 4.0์ ์ ๋๋ฌํ๋์ง ํ์ธ
- ๊ต์ก: ํ์๋ค์ ํ๊ท ์ ์๊ฐ ์ ๊ตญ ํ๊ท 75์ ๊ณผ ๋ค๋ฅธ์ง ๊ฒ์
- ์๋น์ค: ํ๊ท ์๋ต ์๊ฐ์ด SLA ๊ธฐ์ค 3์ด ์ด๋ด์ธ์ง ํ์ธ
๐ก ํต์ฌ ์ง๋ฌธ: โ์ฐ๋ฆฌ ํ๋ณธ์ ํ๊ท ์ด ํน์ ๊ธฐ์ค๊ฐ๊ณผ ๊ฐ์๊ฐ/๋ค๋ฅธ๊ฐ?โ
# Tips: ํ๊ท ํ์ด $3์ธ์ง ๊ฒ์
# ์ํฉ: ๋ ์คํ ๋ ๋งค๋์ ๊ฐ "์ฐ๋ฆฌ ๊ฐ๊ฒ ํ๊ท ํ์ $3์ด๋ค"๋ผ๊ณ ์ฃผ์ฅ. ์ด๊ฒ ๋ง๋์ง ๊ฒ์ฆ.
tip_values = tips['tip']
hypothesized_mean = 3.0
stat, p_value = ttest_1samp(tip_values, hypothesized_mean)
print("=== ๋จ์ผํ๋ณธ t-๊ฒ์ ===")
print(f"Hโ: ํ๊ท ํ = ${hypothesized_mean:.2f}")
print(f"Hโ: ํ๊ท ํ โ ${hypothesized_mean:.2f}")
print(f"\nํ๋ณธ ํ๊ท : ${tip_values.mean():.2f}")
print(f"ํ๋ณธ ํ์คํธ์ฐจ: ${tip_values.std():.2f}")
print(f"t-ํต๊ณ๋: {stat:.4f}")
print(f"p-value: {p_value:.4f}")
print(f"\n๊ฒฐ๋ก : {'ํ๊ท ํ์ $3์ ๋ค๋ฆ' if p_value < 0.05 else 'ํ๊ท ํ์ $3์ ๊ฐ๋ค๊ณ ๋ณผ ์ ์์'}")=== ๋จ์ผํ๋ณธ t-๊ฒ์ === Hโ: ํ๊ท ํ = $3.00 Hโ: ํ๊ท ํ โ $3.00 ํ๋ณธ ํ๊ท : $3.00 ํ๋ณธ ํ์คํธ์ฐจ: $1.38 t-ํต๊ณ๋: -0.0363 p-value: 0.9711 ๊ฒฐ๋ก : ํ๊ท ํ์ $3์ ๊ฐ๋ค๊ณ ๋ณผ ์ ์์
๋ ๋ฆฝํ๋ณธ t-๊ฒ์ (Independent Samples T-Test)
๐ ์ฌ์ฉ ์ํฉ ์์
- A/B ํ ์คํธ: ์ ์น์ฌ์ดํธ ๋์์ธ(B)์ด ๊ธฐ์กด ๋์์ธ(A)๋ณด๋ค ์ ํ์จ์ด ๋์์ง ๊ฒ์ฆ
- ์ํ: ์ ์ฝ ํฌ์ฌ๊ตฐ๊ณผ ์์ฝ๊ตฐ์ ํ์ ๋ณํ ๋น๊ต
- ๊ต์ก: ์จ๋ผ์ธ ์์ ๊ณผ ์คํ๋ผ์ธ ์์ ์ ์ฑ์ ์ฐจ์ด ๋น๊ต
- HR: ์ฌํ๊ทผ๋ฌด์์ ์ฌ๋ฌด์ค ๊ทผ๋ฌด์์ ์์ฐ์ฑ ์ฐจ์ด ๋ถ์
- ๋ง์ผํ : ๋จ์ฑ ๊ณ ๊ฐ๊ณผ ์ฌ์ฑ ๊ณ ๊ฐ์ ํ๊ท ๊ตฌ๋งค ๊ธ์ก ๋น๊ต
๐ก ํต์ฌ ์ง๋ฌธ: โ์๋ก ๋ค๋ฅธ ๋ ๊ทธ๋ฃน์ ํ๊ท ์ด ๊ฐ์๊ฐ/๋ค๋ฅธ๊ฐ?โ
โ ๏ธ ์ฃผ์: ๋ ๊ทธ๋ฃน์ ์๋ก ๋ ๋ฆฝ์ ์ด์ด์ผ ํจ (๊ฐ์ ์ฌ๋์ด ๋ ๊ทธ๋ฃน์ ์ํ๋ฉด ์ ๋จ)
# Titanic: ์ฑ๋ณ์ ๋ฐ๋ฅธ ์ด์ ๋น๊ต
# ์ํฉ: ํ์ดํ๋ํธ์์ ๋จ์ฑ๊ณผ ์ฌ์ฑ์ด ์ง๋ถํ ์ด์์ ์ฐจ์ด๊ฐ ์์๋์ง ๋ถ์
male_fare = titanic[titanic['sex'] == 'male']['fare'].dropna()
female_fare = titanic[titanic['sex'] == 'female']['fare'].dropna()
# ๋ฑ๋ถ์ฐ ๊ฐ์ ๊ฒ์
_, levene_p = levene(male_fare, female_fare)
equal_var = levene_p >= 0.05
# t-๊ฒ์ (Welch's t-test if unequal variance)
stat, p_value = ttest_ind(male_fare, female_fare, equal_var=equal_var)
print("=== ๋
๋ฆฝํ๋ณธ t-๊ฒ์ ===")
print(f"Hโ: ๋จ์ฑ ํ๊ท ์ด์ = ์ฌ์ฑ ํ๊ท ์ด์")
print(f"Hโ: ๋จ์ฑ ํ๊ท ์ด์ โ ์ฌ์ฑ ํ๊ท ์ด์")
print(f"\n๋จ์ฑ ํ๊ท ์ด์: ${male_fare.mean():.2f} (n={len(male_fare)})")
print(f"์ฌ์ฑ ํ๊ท ์ด์: ${female_fare.mean():.2f} (n={len(female_fare)})")
print(f"์ฐจ์ด: ${female_fare.mean() - male_fare.mean():.2f}")
print(f"\n๋ฑ๋ถ์ฐ ๊ฐ์ : {'์ถฉ์กฑ (Student t-test)' if equal_var else '๋ถ์ถฉ์กฑ (Welch t-test ์ฌ์ฉ)'}")
print(f"t-ํต๊ณ๋: {stat:.4f}")
print(f"p-value: {p_value:.4f}")
print(f"\n๊ฒฐ๋ก : {'์ฑ๋ณ์ ๋ฐ๋ฅธ ์ด์ ์ฐจ์ด๊ฐ ์ ์ํจ' if p_value < 0.05 else '์ฑ๋ณ์ ๋ฐ๋ฅธ ์ด์ ์ฐจ์ด ์์'}")=== ๋ ๋ฆฝํ๋ณธ t-๊ฒ์ === Hโ: ๋จ์ฑ ํ๊ท ์ด์ = ์ฌ์ฑ ํ๊ท ์ด์ Hโ: ๋จ์ฑ ํ๊ท ์ด์ โ ์ฌ์ฑ ํ๊ท ์ด์ ๋จ์ฑ ํ๊ท ์ด์: $25.52 (n=577) ์ฌ์ฑ ํ๊ท ์ด์: $44.48 (n=314) ์ฐจ์ด: $18.95 ๋ฑ๋ถ์ฐ ๊ฐ์ : ๋ถ์ถฉ์กฑ (Welch t-test ์ฌ์ฉ) t-ํต๊ณ๋: -4.7994 p-value: 0.0000 ๊ฒฐ๋ก : ์ฑ๋ณ์ ๋ฐ๋ฅธ ์ด์ ์ฐจ์ด๊ฐ ์ ์ํจ
๋์ํ๋ณธ t-๊ฒ์ (Paired Samples T-Test)
๐ ์ฌ์ฉ ์ํฉ ์์
- ๋ค์ด์ดํธ ํจ๊ณผ: ๊ฐ์ ์ฌ๋๋ค์ ๋ค์ด์ดํธ ์ ํ ์ฒด์ค ๋น๊ต
- ๊ต์ก ํจ๊ณผ: ๊ฐ์ ํ์๋ค์ ์์ ์ ํ ์ํ ์ ์ ๋น๊ต
- ์ฝ๋ฌผ ํจ๊ณผ: ๊ฐ์ ํ์์ ํฌ์ฝ ์ ํ ํ์ ๋น๊ต
- UX ๊ฐ์ : ๊ฐ์ ์ฌ์ฉ์๊ฐ ๊ตฌ๋ฒ์ /์ ๋ฒ์ ์ฑ์ ์ฌ์ฉํ์ ๋ ์์ ์๋ฃ ์๊ฐ ๋น๊ต
- ๋ง์ผํ : ๊ฐ์ ๋งค์ฅ์ ํ๋ก๋ชจ์ ์ ํ ๋งค์ถ ๋น๊ต
๐ก ํต์ฌ ์ง๋ฌธ: โ๊ฐ์ ๋์์ ์ฒ์น ์ ํ ๊ฐ์ด ๋ฌ๋ผ์ก๋๊ฐ?โ
โ ๏ธ ๋ ๋ฆฝํ๋ณธ๊ณผ์ ์ฐจ์ด: ๋์ํ๋ณธ์ ๊ฐ์ ๋์์ ๋ ๋ฒ ์ธก์ ํ๋ฏ๋ก ๊ฐ์ธ์ฐจ๋ฅผ ํต์ ํ ์ ์์ด ๋ ๋ฏผ๊ฐํ๊ฒ ๋ณํ๋ฅผ ํ์ง
# ์๋ฎฌ๋ ์ด์
: A/B ํ
์คํธ ์ ํ์จ (๊ฐ์ ์ฌ์ฉ์๊ฐ ๋ ๊ฐ์ง UI๋ฅผ ๊ฒฝํ)
# ์ํฉ: 100๋ช
์ ์ฌ์ฉ์์๊ฒ ๊ตฌ๋ฒ์ UI์ ์ ๋ฒ์ UI๋ฅผ ์์ฐจ์ ์ผ๋ก ๋ณด์ฌ์ฃผ๊ณ ํด๋ฆญ๋ฅ ์ธก์
np.random.seed(42)
n_users = 100
# Before: ๊ธฐ์กด UI ํด๋ฆญ๋ฅ
before = np.random.beta(2, 8, n_users) # ํ๊ท ์ฝ 20%
# After: ์ UI ํด๋ฆญ๋ฅ (์ฝ๊ฐ์ ๊ฐ์ )
after = before + np.random.normal(0.05, 0.03, n_users)
after = np.clip(after, 0, 1)
stat, p_value = ttest_rel(before, after)
print("=== ๋์ํ๋ณธ t-๊ฒ์ ===")
print(f"Hโ: ์ ํ์จ ๋ณํ ์์ (์ UI ํจ๊ณผ ์์)")
print(f"Hโ: ์ ํ์จ ๋ณํ ์์ (์ UI ํจ๊ณผ ์์)")
print(f"\nBefore (๊ธฐ์กด UI) ํ๊ท : {before.mean():.4f} ({before.mean()*100:.1f}%)")
print(f"After (์ UI) ํ๊ท : {after.mean():.4f} ({after.mean()*100:.1f}%)")
print(f"ํ๊ท ์ฐจ์ด: {(after - before).mean():.4f} (+{(after - before).mean()*100:.1f}%p)")
print(f"\nt-ํต๊ณ๋: {stat:.4f}")
print(f"p-value: {p_value:.4f}")
print(f"\n๊ฒฐ๋ก : {'์ UI๊ฐ ์ ์๋ฏธํ๊ฒ ๊ฐ์ ๋จ' if p_value < 0.05 else '์ ์๋ฏธํ ๋ณํ ์์'}")=== ๋์ํ๋ณธ t-๊ฒ์ === Hโ: ์ ํ์จ ๋ณํ ์์ (์ UI ํจ๊ณผ ์์) Hโ: ์ ํ์จ ๋ณํ ์์ (์ UI ํจ๊ณผ ์์) Before (๊ธฐ์กด UI) ํ๊ท : 0.2037 (20.4%) After (์ UI) ํ๊ท : 0.2503 (25.0%) ํ๊ท ์ฐจ์ด: 0.0466 (+4.7%p) t-ํต๊ณ๋: -14.7221 p-value: 0.0000 ๊ฒฐ๋ก : ์ UI๊ฐ ์ ์๋ฏธํ๊ฒ ๊ฐ์ ๋จ
5. ๋น๋ชจ์ ๊ฒ์ (Non-parametric Tests)
๐ฏ ์ธ์ ์ฌ์ฉํ๋์?
- ๋ฐ์ดํฐ๊ฐ ์ ๊ท๋ถํฌ๋ฅผ ๋ฐ๋ฅด์ง ์์ ๋
- ํ๋ณธ ํฌ๊ธฐ๊ฐ ์์ ๋ (n < 30)
- ์์ ๋ฐ์ดํฐ๋ ์์ด ์ฒ๋ ๋ฐ์ดํฐ์ผ ๋
- ์ด์์น(outlier)๊ฐ ๋ง์ ๋ (๋น๋ชจ์ ๊ฒ์ ์ ์ด์์น์ ๋ ๋ฏผ๊ฐ)
Mann-Whitney U Test
๐ ์ฌ์ฉ ์ํฉ ์์
- ์ ์์๊ฑฐ๋: ํ๋ฆฌ๋ฏธ์ ํ์๊ณผ ์ผ๋ฐ ํ์์ ์ฃผ๋ฌธ ๊ธ์ก ๋ถํฌ ๋น๊ต (๊ธ์ก ๋ฐ์ดํฐ๋ ๋ณดํต ์ค๋ฅธ์ชฝ ๊ผฌ๋ฆฌ๊ฐ ๊ธด ๋น์ ๊ท ๋ถํฌ)
- ๊ฒ์: ๊ณผ๊ธ ์ ์ ์ ๋ฌด๊ณผ๊ธ ์ ์ ์ ํ๋ ์ด ์๊ฐ ๋น๊ต
- ์ํ: ๋ ์น๋ฃ๋ฒ์ ํต์ฆ ์ฒ๋(1-10) ๋น๊ต
- ๋ง์กฑ๋: ๋ ์ ํ์ ๊ณ ๊ฐ ํ์ ๋ถํฌ ๋น๊ต
๐ก ํต์ฌ ์ง๋ฌธ: โ๋ ๋ ๋ฆฝ ๊ทธ๋ฃน์ **๋ถํฌ(์์)**๊ฐ ๋ค๋ฅธ๊ฐ?โ
โ ๏ธ ๋ ๋ฆฝํ๋ณธ t-๊ฒ์ ์ ๋น๋ชจ์ ๋์. ํ๊ท ์ด ์๋ ์ค์๊ฐ/์์๋ฅผ ๋น๊ตํ๋ค๊ณ ์๊ฐํ๋ฉด ๋จ.
# Diamonds: ์ปคํ
ํ์ง์ ๋ฐ๋ฅธ ๊ฐ๊ฒฉ ๋น๊ต (Ideal vs Good)
# ์ํฉ: ๋ค์ด์๋ชฌ๋ ์ปคํ
ํ์ง์ด Ideal์ธ ๊ฒ๊ณผ Good์ธ ๊ฒ์ ๊ฐ๊ฒฉ ๋ถํฌ ๋น๊ต
# ๊ฐ๊ฒฉ ๋ฐ์ดํฐ๋ ์ผ๋ฐ์ ์ผ๋ก ์ ๊ท๋ถํฌ๋ฅผ ๋ฐ๋ฅด์ง ์์ผ๋ฏ๋ก ๋น๋ชจ์ ๊ฒ์ ์ฌ์ฉ
ideal_price = diamonds[diamonds['cut'] == 'Ideal']['price']
good_price = diamonds[diamonds['cut'] == 'Good']['price']
stat, p_value = mannwhitneyu(ideal_price, good_price, alternative='two-sided')
print("=== Mann-Whitney U ๊ฒ์ ===")
print(f"Hโ: Ideal ์ปคํ
๊ณผ Good ์ปคํ
์ ๊ฐ๊ฒฉ ๋ถํฌ๊ฐ ๊ฐ๋ค")
print(f"Hโ: ๊ฐ๊ฒฉ ๋ถํฌ๊ฐ ๋ค๋ฅด๋ค")
print(f"\nIdeal ์ค์๊ฐ: ${ideal_price.median():,.2f} (n={len(ideal_price)})")
print(f"Good ์ค์๊ฐ: ${good_price.median():,.2f} (n={len(good_price)})")
print(f"\nU-ํต๊ณ๋: {stat:,.2f}")
print(f"p-value: {p_value:.4f}")
print(f"\n๊ฒฐ๋ก : {'๊ฐ๊ฒฉ ๋ถํฌ๊ฐ ์ ์ํ๊ฒ ๋ค๋ฆ' if p_value < 0.05 else '๊ฐ๊ฒฉ ๋ถํฌ ์ฐจ์ด ์์'}")=== Mann-Whitney U ๊ฒ์ === Hโ: Ideal ์ปคํ ๊ณผ Good ์ปคํ ์ ๊ฐ๊ฒฉ ๋ถํฌ๊ฐ ๊ฐ๋ค Hโ: ๊ฐ๊ฒฉ ๋ถํฌ๊ฐ ๋ค๋ฅด๋ค Ideal ์ค์๊ฐ: $1,810.00 (n=393) Good ์ค์๊ฐ: $3,086.50 (n=96) U-ํต๊ณ๋: 14,985.00 p-value: 0.0031 ๊ฒฐ๋ก : ๊ฐ๊ฒฉ ๋ถํฌ๊ฐ ์ ์ํ๊ฒ ๋ค๋ฆ
Wilcoxon Signed-Rank Test
๐ ์ฌ์ฉ ์ํฉ ์์
- ์ฒด์ค ๊ฐ๋: ๋ค์ด์ดํธ ํ๋ก๊ทธ๋จ ์ ํ ์ฒด์ค ๋น๊ต (์ฒด์ค ๋ณํ๊ฐ ์ ๊ท๋ถํฌ๊ฐ ์๋ ๋)
- ์ค๋ฌธ์กฐ์ฌ: ๊ฐ์ ์๋ต์์ ์ ์ฑ ๋ณ๊ฒฝ ์ ํ ๋ง์กฑ๋(1-5์ ) ๋น๊ต
- ๊ต์ก: ๊ฐ์ ํ์์ ํน๊ฐ ์ ํ ์์ ๊ฐ ์ ์ ๋น๊ต
- ์ฑ ํ์ : ์ ๋ฐ์ดํธ ์ ํ ๊ฐ์ ์ฌ์ฉ์์ ํ์ ๋ณํ
๐ก ํต์ฌ ์ง๋ฌธ: โ๊ฐ์ ๋์์ ์ ํ ๊ฐ ๋ถํฌ๊ฐ ๋ฌ๋ผ์ก๋๊ฐ?โ
โ ๏ธ ๋์ํ๋ณธ t-๊ฒ์ ์ ๋น๋ชจ์ ๋์. ์ฐจ์ด๊ฐ์ ๋ถํธ์ ์์๋ฅผ ์ฌ์ฉ.
# Tips: ์ ์ฌ vs ์ ๋
ํ๋ฅ ๋น๊ต (๊ฐ์ ์จ์ดํฐ)
# ์ํฉ: 50๋ช
์ ์จ์ดํฐ๊ฐ ์ ์ฌ๊ณผ ์ ๋
์ ๋ฐ๋ ํ ๋น์จ์ด ๋ค๋ฅธ์ง ๊ฒ์
np.random.seed(42)
n_waiters = 50
lunch_tip_rate = np.random.uniform(0.12, 0.22, n_waiters)
dinner_tip_rate = lunch_tip_rate + np.random.normal(0.02, 0.03, n_waiters)
stat, p_value = wilcoxon(lunch_tip_rate, dinner_tip_rate)
print("=== Wilcoxon Signed-Rank ๊ฒ์ ===")
print(f"Hโ: ์ ์ฌ๊ณผ ์ ๋
ํ๋ฅ ์ฐจ์ด ์์")
print(f"Hโ: ์ ์ฌ๊ณผ ์ ๋
ํ๋ฅ ์ฐจ์ด ์์")
print(f"\n์ ์ฌ ํ๋ฅ ์ค์๊ฐ: {np.median(lunch_tip_rate)*100:.1f}%")
print(f"์ ๋
ํ๋ฅ ์ค์๊ฐ: {np.median(dinner_tip_rate)*100:.1f}%")
print(f"\nW-ํต๊ณ๋: {stat:.2f}")
print(f"p-value: {p_value:.4f}")
print(f"\n๊ฒฐ๋ก : {'์ ๋
ํ๋ฅ ์ด ์ ์ํ๊ฒ ๋์' if p_value < 0.05 else '์ฐจ์ด ์์'}")=== Wilcoxon Signed-Rank ๊ฒ์ === Hโ: ์ ์ฌ๊ณผ ์ ๋ ํ๋ฅ ์ฐจ์ด ์์ Hโ: ์ ์ฌ๊ณผ ์ ๋ ํ๋ฅ ์ฐจ์ด ์์ ์ ์ฌ ํ๋ฅ ์ค์๊ฐ: 16.9% ์ ๋ ํ๋ฅ ์ค์๊ฐ: 19.1% W-ํต๊ณ๋: 304.00 p-value: 0.0004 ๊ฒฐ๋ก : ์ ๋ ํ๋ฅ ์ด ์ ์ํ๊ฒ ๋์
Kruskal-Wallis H Test
๐ ์ฌ์ฉ ์ํฉ ์์
- ๋ง์ผํ : 3๊ฐ์ง ๊ด๊ณ ์ ํ(TV, ์จ๋ผ์ธ, SNS)๋ณ ๋ธ๋๋ ์ธ์ง๋ ์ ์ ๋น๊ต
- ์ ํ: ์ฌ๋ฌ ๋ธ๋๋ ์ค๋งํธํฐ์ ๊ณ ๊ฐ ๋ง์กฑ๋ ๋น๊ต
- ๊ต์ก: 3๊ฐ ํ๊ต์ ํ์ ์ฑ์ ๋ถํฌ ๋น๊ต
- ์ํ: ์ฌ๋ฌ ์น๋ฃ๋ฒ์ ํ๋ณต ๊ธฐ๊ฐ ๋น๊ต (๋น์ ๊ท ๋ฐ์ดํฐ)
๐ก ํต์ฌ ์ง๋ฌธ: โ3๊ฐ ์ด์ ๊ทธ๋ฃน์ ๋ถํฌ๊ฐ ๋ชจ๋ ๊ฐ์๊ฐ, ์๋๋ฉด ์ ์ด๋ ํ๋๊ฐ ๋ค๋ฅธ๊ฐ?โ
โ ๏ธ One-Way ANOVA์ ๋น๋ชจ์ ๋์. ์ด๋ค ๊ทธ๋ฃน์ด ๋ค๋ฅธ์ง๋ ์ฌํ๊ฒ์ ํ์.
# Iris: ํ์ข
๋ณ ๊ฝ์ ๋๋น ๋น๊ต
# ์ํฉ: ์ธ ๊ฐ์ง ๋ถ๊ฝ ํ์ข
(setosa, versicolor, virginica)์ ๊ฝ์ ๋๋น ๋ถํฌ๊ฐ ๋ค๋ฅธ์ง ๊ฒ์
setosa_pw = iris[iris['species'] == 'setosa']['petal_width']
versicolor_pw = iris[iris['species'] == 'versicolor']['petal_width']
virginica_pw = iris[iris['species'] == 'virginica']['petal_width']
stat, p_value = kruskal(setosa_pw, versicolor_pw, virginica_pw)
print("=== Kruskal-Wallis H ๊ฒ์ ===")
print(f"Hโ: ๋ชจ๋ ํ์ข
์ ๊ฝ์ ๋๋น ๋ถํฌ๊ฐ ๊ฐ๋ค")
print(f"Hโ: ์ ์ด๋ ํ๋์ ํ์ข
์ด ๋ค๋ฅด๋ค")
print(f"\nSetosa ์ค์๊ฐ: {setosa_pw.median():.2f}cm")
print(f"Versicolor ์ค์๊ฐ: {versicolor_pw.median():.2f}cm")
print(f"Virginica ์ค์๊ฐ: {virginica_pw.median():.2f}cm")
print(f"\nH-ํต๊ณ๋: {stat:.4f}")
print(f"p-value: {p_value:.6f}")
print(f"\n๊ฒฐ๋ก : {'ํ์ข
๊ฐ ์ ์ํ ์ฐจ์ด ์์' if p_value < 0.05 else '์ฐจ์ด ์์'}")=== Kruskal-Wallis H ๊ฒ์ === Hโ: ๋ชจ๋ ํ์ข ์ ๊ฝ์ ๋๋น ๋ถํฌ๊ฐ ๊ฐ๋ค Hโ: ์ ์ด๋ ํ๋์ ํ์ข ์ด ๋ค๋ฅด๋ค Setosa ์ค์๊ฐ: 0.20cm Versicolor ์ค์๊ฐ: 1.30cm Virginica ์ค์๊ฐ: 2.00cm H-ํต๊ณ๋: 130.0111 p-value: 0.000000 ๊ฒฐ๋ก : ํ์ข ๊ฐ ์ ์ํ ์ฐจ์ด ์์
6. ๋ถ์ฐ๋ถ์ (ANOVA)
์ผ์๋ถ์ฐ๋ถ์ (One-Way ANOVA)
๐ ์ฌ์ฉ ์ํฉ ์์
- ๋ง์ผํ : 4๊ฐ์ง ํ๋ก๋ชจ์ ์ ํ(ํ ์ธ, ์ ๋ฆฝ, ์ฌ์ํ, ๋ฌด๋ฃ๋ฐฐ์ก)์ ํ๊ท ๊ตฌ๋งค์ก ๋น๊ต
- ์ ์กฐ: 3๊ฐ ๊ณต์ฅ์์ ์์ฐ๋ ์ ํ์ ํ๊ท ํ์ง ์ ์ ๋น๊ต
- HR: ๋ถ์๋ณ(๊ฐ๋ฐ, ๋ง์ผํ , ์์ , ์ง์) ์ง์ ๋ง์กฑ๋ ๋น๊ต
- ๊ต์ก: ์ฌ๋ฌ ๊ต์๋ฒ์ ํ์ต ํจ๊ณผ ๋น๊ต
๐ก ํต์ฌ ์ง๋ฌธ: โ3๊ฐ ์ด์ ๊ทธ๋ฃน์ ํ๊ท ์ด ๋ชจ๋ ๊ฐ์๊ฐ?โ
โ ๏ธ ์ ์ ์กฐ๊ฑด: ์ ๊ท์ฑ, ๋ฑ๋ถ์ฐ์ฑ. ์๋ฐฐ ์ Kruskal-Wallis ์ฌ์ฉ.
# Tips: ์์ผ๋ณ ์ด ๊ฒฐ์ ๊ธ์ก ๋น๊ต
# ์ํฉ: ์์ผ์ ๋ฐ๋ผ ๊ณ ๊ฐ๋ค์ ํ๊ท ๊ฒฐ์ ๊ธ์ก์ด ๋ค๋ฅธ์ง ๋ถ์
thur = tips[tips['day'] == 'Thur']['total_bill']
fri = tips[tips['day'] == 'Fri']['total_bill']
sat = tips[tips['day'] == 'Sat']['total_bill']
sun = tips[tips['day'] == 'Sun']['total_bill']
stat, p_value = f_oneway(thur, fri, sat, sun)
print("=== One-Way ANOVA ===")
print(f"Hโ: ๋ชจ๋ ์์ผ์ ํ๊ท ๊ฒฐ์ ๊ธ์ก์ด ๊ฐ๋ค")
print(f"Hโ: ์ ์ด๋ ํ๋์ ์์ผ์ด ๋ค๋ฅด๋ค")
print(f"\n์์ผ๋ณ ํ๊ท ๊ฒฐ์ ๊ธ์ก:")
print(f" ๋ชฉ์์ผ: ${thur.mean():.2f} (n={len(thur)})")
print(f" ๊ธ์์ผ: ${fri.mean():.2f} (n={len(fri)})")
print(f" ํ ์์ผ: ${sat.mean():.2f} (n={len(sat)})")
print(f" ์ผ์์ผ: ${sun.mean():.2f} (n={len(sun)})")
print(f"\nF-ํต๊ณ๋: {stat:.4f}")
print(f"p-value: {p_value:.4f}")
print(f"\n๊ฒฐ๋ก : {'์์ผ๋ณ ์ ์ํ ์ฐจ์ด ์์ โ ์ฌํ๊ฒ์ ํ์' if p_value < 0.05 else '์์ผ๋ณ ์ฐจ์ด ์์'}")=== One-Way ANOVA === Hโ: ๋ชจ๋ ์์ผ์ ํ๊ท ๊ฒฐ์ ๊ธ์ก์ด ๊ฐ๋ค Hโ: ์ ์ด๋ ํ๋์ ์์ผ์ด ๋ค๋ฅด๋ค ์์ผ๋ณ ํ๊ท ๊ฒฐ์ ๊ธ์ก: ๋ชฉ์์ผ: $17.68 (n=62) ๊ธ์์ผ: $17.15 (n=19) ํ ์์ผ: $20.44 (n=87) ์ผ์์ผ: $21.41 (n=76) F-ํต๊ณ๋: 2.7675 p-value: 0.0424 ๊ฒฐ๋ก : ์์ผ๋ณ ์ ์ํ ์ฐจ์ด ์์ โ ์ฌํ๊ฒ์ ํ์
์ด์๋ถ์ฐ๋ถ์ (Two-Way ANOVA)
๐ ์ฌ์ฉ ์ํฉ ์์
- ๋ง์ผํ : **๊ด๊ณ ์ฑ๋(TV/์จ๋ผ์ธ)**๊ณผ **ํ๊ฒ ์ฐ๋ น๋(20๋/30๋/40๋)**๊ฐ ๊ตฌ๋งค ์ํฅ์ ๋ฏธ์น๋ ์ํฅ ๋ถ์
- ์ ์กฐ: ๊ธฐ๊ณ ์ข ๋ฅ์ ์์ ์ ์๋ จ๋๊ฐ ์์ฐ๋์ ๋ฏธ์น๋ ์ํฅ
- ๊ต์ก: ๊ต์๋ฒ๊ณผ ํ๊ธ ๊ท๋ชจ๊ฐ ํ์ต ์ฑ๊ณผ์ ๋ฏธ์น๋ ์ํฅ
- ์ํ: ์ฝ๋ฌผ ์ข ๋ฅ์ ํฌ์ฝ ์ฉ๋์ด ์น๋ฃ ํจ๊ณผ์ ๋ฏธ์น๋ ์ํฅ
๐ก ํต์ฌ ์ง๋ฌธ:
- ์์ธ A์ ์ฃผํจ๊ณผ๊ฐ ์๋๊ฐ?
- ์์ธ B์ ์ฃผํจ๊ณผ๊ฐ ์๋๊ฐ?
- A์ B์ ์ํธ์์ฉ ํจ๊ณผ๊ฐ ์๋๊ฐ? (์: ํน์ ์กฐํฉ์์๋ง ํจ๊ณผ๊ฐ ์๋์ง)
import statsmodels.api as sm
from statsmodels.formula.api import ols
# Tips: ์ฑ๋ณ๊ณผ ํก์ฐ ์ฌ๋ถ๊ฐ ํ์ ๋ฏธ์น๋ ์ํฅ
# ์ํฉ: ํ ๊ธ์ก์ด ์ฑ๋ณ๊ณผ ํก์ฐ ์ฌ๋ถ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋์ง, ๊ทธ๋ฆฌ๊ณ ์ด ๋์ ์กฐํฉ ํจ๊ณผ๊ฐ ์๋์ง ๋ถ์
model = ols('tip ~ C(sex) + C(smoker) + C(sex):C(smoker)', data=tips).fit()
anova_table = sm.stats.anova_lm(model, typ=2)
print("=== Two-Way ANOVA ===")
print(f"์ข
์๋ณ์: ํ ๊ธ์ก")
print(f"์์ธ 1: ์ฑ๋ณ (sex)")
print(f"์์ธ 2: ํก์ฐ ์ฌ๋ถ (smoker)")
print(f"\n{anova_table.round(4)}")
print("\nํด์:")
for idx, row in anova_table.iterrows():
if idx != 'Residual':
sig = "์ ์ํจ ***" if row['PR(>F)'] < 0.001 else \
"์ ์ํจ **" if row['PR(>F)'] < 0.01 else \
"์ ์ํจ *" if row['PR(>F)'] < 0.05 else "์ ์ํ์ง ์์"
print(f" {idx}: p = {row['PR(>F)']:.4f} โ {sig}")=== Two-Way ANOVA ===
์ข
์๋ณ์: ํ ๊ธ์ก
์์ธ 1: ์ฑ๋ณ (sex)
์์ธ 2: ํก์ฐ ์ฌ๋ถ (smoker)
sum_sq df F PR(>F)
C(sex) 1.0554 1.0 0.5596 0.4551
C(smoker) 0.1477 1.0 0.0783 0.7799
C(sex):C(smoker) 0.2077 1.0 0.1101 0.7404
Residual 452.5604 240.0 NaN NaN
ํด์:
C(sex): p = 0.4551 โ ์ ์ํ์ง ์์
C(smoker): p = 0.7799 โ ์ ์ํ์ง ์์
C(sex):C(smoker): p = 0.7404 โ ์ ์ํ์ง ์์7. ์นด์ด์ ๊ณฑ ๊ฒ์ (Chi-Square Tests)
๋ ๋ฆฝ์ฑ ๊ฒ์ (Test of Independence)
๐ ์ฌ์ฉ ์ํฉ ์์
- ๋ง์ผํ : ์ฐ๋ น๋(20๋/30๋/40๋)์ ์ ํธ ๋ธ๋๋(A/B/C)๊ฐ ์ฐ๊ด๋์ด ์๋์ง ๋ถ์
- ์ํ: ํก์ฐ ์ฌ๋ถ์ ํ์ ๋ฐ์์ด ์ฐ๊ด๋์ด ์๋์ง ๊ฒ์
- ๊ต์ก: ์ฑ๋ณ๊ณผ ์ ๊ณต ์ ํ์ด ์ฐ๊ด๋์ด ์๋์ง ๋ถ์
- HR: ํ๋ ฅ๊ณผ ์ด์ง ์ฌ๋ถ๊ฐ ์ฐ๊ด๋์ด ์๋์ง ๋ถ์
- ์ ๊ฑฐ: ์ง์ญ๊ณผ ์ง์ง ์ ๋น์ด ์ฐ๊ด๋์ด ์๋์ง ๋ถ์
๐ก ํต์ฌ ์ง๋ฌธ: โ๋ ๋ฒ์ฃผํ ๋ณ์๊ฐ ์๋ก ๋ ๋ฆฝ์ธ๊ฐ, ์ฐ๊ด๋์ด ์๋๊ฐ?โ
# Titanic: ์ฑ๋ณ๊ณผ ์์กด ์ฌ๋ถ์ ๊ด๊ณ
# ์ํฉ: ํ์ดํ๋ํธ ์นจ๋ชฐ ์ ์ฑ๋ณ์ ๋ฐ๋ผ ์์กด์จ์ด ๋ฌ๋๋์ง ๋ถ์ ("์ฌ์ฑ๊ณผ ์์ด ๋จผ์ " ๊ท์น)
contingency = pd.crosstab(titanic['sex'], titanic['survived'])
print("๊ต์ฐจํ:")
print(contingency)
print()
chi2, p_value, dof, expected = chi2_contingency(contingency)
print("=== ์นด์ด์ ๊ณฑ ๋
๋ฆฝ์ฑ ๊ฒ์ ===")
print(f"Hโ: ์ฑ๋ณ๊ณผ ์์กด ์ฌ๋ถ๋ ๋
๋ฆฝ์ ์ด๋ค (๊ด๋ จ ์์)")
print(f"Hโ: ์ฑ๋ณ๊ณผ ์์กด ์ฌ๋ถ๋ ์ฐ๊ด์ด ์๋ค")
print(f"\nฯยฒ ํต๊ณ๋: {chi2:.4f}")
print(f"์์ ๋: {dof}")
print(f"p-value: {p_value:.6f}")
print(f"\n๊ธฐ๋๋น๋ (๋
๋ฆฝ์ด๋ผ๋ฉด ์ด ์ ๋๊ฐ ์์๋จ):")
print(pd.DataFrame(expected,
index=contingency.index,
columns=contingency.columns).round(1))
print(f"\n๊ฒฐ๋ก : {'์ฑ๋ณ๊ณผ ์์กด์ ๊ฐํ๊ฒ ์ฐ๊ด๋จ (์ฌ์ฑ ์์กด์จ์ด ๋์)' if p_value < 0.05 else '๋
๋ฆฝ์ '}")๊ต์ฐจํ: survived 0 1 sex female 81 233 male 468 109 === ์นด์ด์ ๊ณฑ ๋ ๋ฆฝ์ฑ ๊ฒ์ === Hโ: ์ฑ๋ณ๊ณผ ์์กด ์ฌ๋ถ๋ ๋ ๋ฆฝ์ ์ด๋ค (๊ด๋ จ ์์) Hโ: ์ฑ๋ณ๊ณผ ์์กด ์ฌ๋ถ๋ ์ฐ๊ด์ด ์๋ค ฯยฒ ํต๊ณ๋: 260.7170 ์์ ๋: 1 p-value: 0.000000 ๊ธฐ๋๋น๋ (๋ ๋ฆฝ์ด๋ผ๋ฉด ์ด ์ ๋๊ฐ ์์๋จ): survived 0 1 sex female 193.5 120.5 male 355.5 221.5 ๊ฒฐ๋ก : ์ฑ๋ณ๊ณผ ์์กด์ ๊ฐํ๊ฒ ์ฐ๊ด๋จ (์ฌ์ฑ ์์กด์จ์ด ๋์)
์ ํฉ๋ ๊ฒ์ (Goodness of Fit)
๐ ์ฌ์ฉ ์ํฉ ์์
- ํ์ง๊ด๋ฆฌ: ๋ถ๋ํ ๋ฐ์์ด ๊ท ๋ฑํ๊ฒ(1/5์ฉ) ๊ฐ ์์ผ์ ๋ถํฌํ๋์ง ๊ฒ์
- ๋ง์ผํ : ๊ณ ๊ฐ ๋ถํฌ๊ฐ **๊ธฐ๋ํ ๋น์จ(40:35:25)**๊ณผ ์ผ์นํ๋์ง ํ์ธ
- ์ ์ ํ: ๊ด์ธก๋ ์ ์ ํ ๋น์จ์ด **๋ฉ๋ธ์ ๋ฒ์น(9:3:3:1)**๊ณผ ๋ง๋์ง ๊ฒ์
- ์ค๋ฌธ: ์๋ต ๋ถํฌ๊ฐ ๊ท ๋ฑ๋ถํฌ๋ฅผ ๋ฐ๋ฅด๋์ง ํ์ธ
๐ก ํต์ฌ ์ง๋ฌธ: โ๊ด์ธก๋ ๋น๋๊ฐ ๊ธฐ๋ํ ์ด๋ก ์ ๋ถํฌ์ ์ผ์นํ๋๊ฐ?โ
# Titanic: ๊ฐ์ค ๋ฑ๊ธ ๋ถํฌ๊ฐ ๊ท ๋ฑํ์ง ๊ฒ์
# ์ํฉ: ํ์ดํ๋ํธ ์น๊ฐ์ด 1,2,3๋ฑ๊ธ์ ๊ท ๋ฑํ๊ฒ ๋ถํฌ๋์ด ์์๋์ง ํ์ธ
observed = titanic['pclass'].value_counts().sort_index()
n = len(titanic)
expected = np.array([n/3, n/3, n/3]) # ๊ท ๋ฑ ๋ถํฌ ๊ธฐ๋
chi2, p_value = stats.chisquare(observed, expected)
print("=== ์นด์ด์ ๊ณฑ ์ ํฉ๋ ๊ฒ์ ===")
print(f"Hโ: ๊ฐ์ค ๋ฑ๊ธ์ ๊ท ๋ฑํ๊ฒ ๋ถํฌ๋์ด ์๋ค (๊ฐ 33.3%)")
print(f"Hโ: ๊ท ๋ฑํ์ง ์๋ค")
print(f"\n๊ด์ธก ๋น๋:")
for cls, count in observed.items():
print(f" {cls}๋ฑ์: {count}๋ช
({count/n*100:.1f}%)")
print(f"\n๊ธฐ๋ ๋น๋ (๊ท ๋ฑ ๋ถํฌ): ๊ฐ {n/3:.0f}๋ช
(33.3%)")
print(f"\nฯยฒ ํต๊ณ๋: {chi2:.4f}")
print(f"p-value: {p_value:.6f}")
print(f"\n๊ฒฐ๋ก : {'๊ท ๋ฑํ์ง ์์ - 3๋ฑ์์ด ๊ณผ๋ฐ' if p_value < 0.05 else '๊ท ๋ฑ ๋ถํฌ'}")=== ์นด์ด์ ๊ณฑ ์ ํฉ๋ ๊ฒ์ === Hโ: ๊ฐ์ค ๋ฑ๊ธ์ ๊ท ๋ฑํ๊ฒ ๋ถํฌ๋์ด ์๋ค (๊ฐ 33.3%) Hโ: ๊ท ๋ฑํ์ง ์๋ค ๊ด์ธก ๋น๋: 1๋ฑ์: 216๋ช (24.2%) 2๋ฑ์: 184๋ช (20.7%) 3๋ฑ์: 491๋ช (55.1%) ๊ธฐ๋ ๋น๋ (๊ท ๋ฑ ๋ถํฌ): ๊ฐ 297๋ช (33.3%) ฯยฒ ํต๊ณ๋: 110.8417 p-value: 0.000000 ๊ฒฐ๋ก : ๊ท ๋ฑํ์ง ์์ - 3๋ฑ์์ด ๊ณผ๋ฐ
Fisherโs Exact Test
๐ ์ฌ์ฉ ์ํฉ ์์
- ์์์ํ: ์๊ท๋ชจ ํ์ผ๋ฟ ์ฐ๊ตฌ(n < 20)์์ ์น๋ฃ ํจ๊ณผ ๊ฒ์
- ํฌ๊ท ์งํ: ๋ฐ์ ๋น๋๊ฐ ๋ฎ์ ํฌ๊ท ์งํ๊ณผ ์ ์ ์ ๋ณ์ด์ ์ฐ๊ด์ฑ
- ํ์ง๊ด๋ฆฌ: ๊ธฐ๋๋น๋๊ฐ 5 ๋ฏธ๋ง์ธ ํฌ๊ท ๋ถ๋ ์ ํ ๋ถ์
- ์ญํ์กฐ์ฌ: ์๊ท๋ชจ ์ง๋จ์์ ๊ฐ์ผ ์ฌ๋ถ์ ํน์ ํ๋์ ์ฐ๊ด์ฑ
๐ก ํต์ฌ ์ง๋ฌธ: โ2ร2 ๊ต์ฐจํ์์ ์ํ๋ณธ์ผ ๋ ๋ ๋ณ์๊ฐ ์ฐ๊ด๋์ด ์๋๊ฐ?โ
โ ๏ธ ์นด์ด์ ๊ณฑ ๊ฒ์ ์ ๋์: ๊ธฐ๋๋น๋๊ฐ 5 ๋ฏธ๋ง์ธ ์ ์ด ์์ผ๋ฉด Fisherโs Exact ์ฌ์ฉ ๊ถ์ฅ
# ์๊ท๋ชจ ์์์ํ ์๋ฎฌ๋ ์ด์
# ์ํฉ: 20๋ช
๋์ ํ์ผ๋ฟ ์ฐ๊ตฌ. ์ ์ฝ ํฌ์ฌ๊ตฐ(10๋ช
)๊ณผ ์์ฝ๊ตฐ(10๋ช
)์ ์์น์จ ๋น๊ต
contingency = np.array([[8, 2], # ์น๋ฃ๊ตฐ: ์์น 8, ๋ฏธ์์น 2
[3, 7]]) # ๋์กฐ๊ตฐ: ์์น 3, ๋ฏธ์์น 7
odds_ratio, p_value = fisher_exact(contingency)
print("=== Fisher's Exact Test ===")
print("์ํฉ: ์๊ท๋ชจ ์์์ํ (n=20)")
print("\n๊ต์ฐจํ:")
print(" ์์น ๋ฏธ์์น")
print(f"์น๋ฃ๊ตฐ {contingency[0,0]} {contingency[0,1]}")
print(f"๋์กฐ๊ตฐ {contingency[1,0]} {contingency[1,1]}")
print(f"\n์น๋ฃ๊ตฐ ์์น์จ: {contingency[0,0]/contingency[0].sum()*100:.0f}%")
print(f"๋์กฐ๊ตฐ ์์น์จ: {contingency[1,0]/contingency[1].sum()*100:.0f}%")
print(f"\nOdds Ratio: {odds_ratio:.4f}")
print(f"p-value: {p_value:.4f}")
print(f"\n๊ฒฐ๋ก : {'์น๋ฃ ํจ๊ณผ ์์' if p_value < 0.05 else '์น๋ฃ ํจ๊ณผ ์์'}")
print(f"\nํด์: ์น๋ฃ๊ตฐ์ด ๋์กฐ๊ตฐ๋ณด๋ค ์์น odds๊ฐ {odds_ratio:.1f}๋ฐฐ ๋์")=== Fisher's Exact Test ===
์ํฉ: ์๊ท๋ชจ ์์์ํ (n=20)
๊ต์ฐจํ:
์์น ๋ฏธ์์น
์น๋ฃ๊ตฐ 8 2
๋์กฐ๊ตฐ 3 7
์น๋ฃ๊ตฐ ์์น์จ: 80%
๋์กฐ๊ตฐ ์์น์จ: 30%
Odds Ratio: 9.3333
p-value: 0.0350
๊ฒฐ๋ก : ์น๋ฃ ํจ๊ณผ ์์
ํด์: ์น๋ฃ๊ตฐ์ด ๋์กฐ๊ตฐ๋ณด๋ค ์์น odds๊ฐ 9.3๋ฐฐ ๋์McNemarโs Test
๐ ์ฌ์ฉ ์ํฉ ์์
- ๋ง์ผํ : ๊ด๊ณ ์บ ํ์ธ ์ ํ ๊ฐ์ ๊ณ ๊ฐ์ ๋ธ๋๋ ์ธ์ง ๋ณํ (์ธ์งOโ์ธ์งO, ์ธ์งXโ์ธ์งO ๋ฑ)
- ์ํ: ๊ฐ์ ํ์์ ์น๋ฃ ์ ํ ์ฆ์ ์ ๋ฌด ๋ณํ
- ์ ์น: ๊ฐ์ ์ ๊ถ์์ ์ ๊ฑฐ ์ ํ ์ง์ง ์ ๋น ๋ณํ
- ๊ต์ก: ๊ฐ์ ํ์์ ์์ ์ ํ ํน์ ๊ฐ๋ ์ดํด ์ฌ๋ถ ๋ณํ
๐ก ํต์ฌ ์ง๋ฌธ: โ๊ฐ์ ๋์์ ๋ฒ์ฃผํ ๋ฐ์์ด ์ ํ๋ก ๋ฌ๋ผ์ก๋๊ฐ?โ
โ ๏ธ ๋์ํ๋ณธ + ๋ฒ์ฃผํ ๋ฐ์ดํฐ์ผ ๋ ์ฌ์ฉ. ์ฐ์ํ์ด๋ฉด Wilcoxon/๋์ t-๊ฒ์ ์ฌ์ฉ.
from statsmodels.stats.contingency_tables import mcnemar
# ๋ง์ผํ
์บ ํ์ธ ์ ํ ๊ตฌ๋งค ํ๋ ๋ณํ
# ์ํฉ: 100๋ช
์ ๊ณ ๊ฐ์ ๋์์ผ๋ก ์บ ํ์ธ ์ ํ ๊ตฌ๋งค ์ฌ๋ถ๋ฅผ ์ถ์
# [์บ ํ์ธ ์ ๊ตฌ๋งคO/ํ ๊ตฌ๋งคO, ์ ๊ตฌ๋งคO/ํ ๊ตฌ๋งคX]
# [์บ ํ์ธ ์ ๊ตฌ๋งคX/ํ ๊ตฌ๋งคO, ์ ๊ตฌ๋งคX/ํ ๊ตฌ๋งคX]
table = np.array([[45, 15], # ์ ์๋ ๊ตฌ๋งค, ํ์๋ ๊ตฌ๋งค / ์ ์ ๊ตฌ๋งค, ํ์ ๋ฏธ๊ตฌ๋งค
[35, 5]]) # ์ ์ ๋ฏธ๊ตฌ๋งค, ํ์ ๊ตฌ๋งค / ์ ์๋ ๋ฏธ๊ตฌ๋งค, ํ์๋ ๋ฏธ๊ตฌ๋งค
result = mcnemar(table, exact=True)
print("=== McNemar's Test ===")
print("์ํฉ: 100๋ช
๊ณ ๊ฐ์ ์บ ํ์ธ ์ ํ ๊ตฌ๋งค ํ๋ ๋ณํ")
print("\n๋์ํ:")
print(" ์บ ํ์ธ ํ ๊ตฌ๋งคO ์บ ํ์ธ ํ ๊ตฌ๋งคX")
print(f"์บ ํ์ธ ์ ๊ตฌ๋งคO {table[0,0]} {table[0,1]}")
print(f"์บ ํ์ธ ์ ๊ตฌ๋งคX {table[1,0]} {table[1,1]}")
print(f"\n๋ณํ ๋ถ์:")
print(f" โ ๊ตฌ๋งคX โ ๊ตฌ๋งคO (์ ๊ท ๊ตฌ๋งค): {table[1,0]}๋ช
")
print(f" โ ๊ตฌ๋งคO โ ๊ตฌ๋งคX (์ดํ): {table[0,1]}๋ช
")
print(f" = ๋ณํ ์์: {table[0,0] + table[1,1]}๋ช
")
print(f"\np-value: {result.pvalue:.4f}")
print(f"\n๊ฒฐ๋ก : {'์บ ํ์ธ์ด ๊ตฌ๋งค ํ๋์ ์ ์ํ๊ฒ ๋ณํ์ํด (์ ๊ท ๊ตฌ๋งค > ์ดํ)' if result.pvalue < 0.05 else '์ ์ํ ๋ณํ ์์'}")=== McNemar's Test ===
์ํฉ: 100๋ช
๊ณ ๊ฐ์ ์บ ํ์ธ ์ ํ ๊ตฌ๋งค ํ๋ ๋ณํ
๋์ํ:
์บ ํ์ธ ํ ๊ตฌ๋งคO ์บ ํ์ธ ํ ๊ตฌ๋งคX
์บ ํ์ธ ์ ๊ตฌ๋งคO 45 15
์บ ํ์ธ ์ ๊ตฌ๋งคX 35 5
๋ณํ ๋ถ์:
โ ๊ตฌ๋งคX โ ๊ตฌ๋งคO (์ ๊ท ๊ตฌ๋งค): 35๋ช
โ ๊ตฌ๋งคO โ ๊ตฌ๋งคX (์ดํ): 15๋ช
= ๋ณํ ์์: 50๋ช
p-value: 0.0066
๊ฒฐ๋ก : ์บ ํ์ธ์ด ๊ตฌ๋งค ํ๋์ ์ ์ํ๊ฒ ๋ณํ์ํด (์ ๊ท ๊ตฌ๋งค > ์ดํ)8. ์๊ด๋ถ์ (Correlation Analysis)
Pearson ์๊ด๊ณ์
๐ ์ฌ์ฉ ์ํฉ ์์
- ๋ง์ผํ : ๊ด๊ณ ๋น ์ง์ถ๊ณผ ๋งค์ถ์ ์ ํ์ ๊ด๊ณ ๋ถ์
- HR: ๊ทผ์ ์ฐ์์ ์ฐ๋ด์ ์๊ด๊ด๊ณ
- ๊ต์ก: ๊ณต๋ถ ์๊ฐ๊ณผ ์ํ ์ ์์ ๊ด๊ณ
- ๊ธ์ต: ๊ธ๋ฆฌ์ ์ฃผ๊ฐ์ ๊ด๊ณ
๐ก ํต์ฌ ์ง๋ฌธ: โ๋ ์ฐ์ํ ๋ณ์ ๊ฐ์ ์ ํ์ ๊ด๊ณ๊ฐ ์๋๊ฐ?โ
โ ๏ธ ์ ์ ์กฐ๊ฑด: ๋ ๋ณ์ ๋ชจ๋ ์ ๊ท๋ถํฌ, ์ ํ ๊ด๊ณ. ๋น์ ํ ๊ด๊ณ๋ ๊ฐ์ง ๋ชปํจ.
# Diamonds: ์บ๋ฟ๊ณผ ๊ฐ๊ฒฉ์ ์๊ด๊ด๊ณ
# ์ํฉ: ๋ค์ด์๋ชฌ๋ ์บ๋ฟ(๋ฌด๊ฒ)๊ณผ ๊ฐ๊ฒฉ ์ฌ์ด์ ์ ํ์ ๊ด๊ณ๊ฐ ์๋์ง ๋ถ์
carat = diamonds['carat']
price = diamonds['price']
r, p_value = pearsonr(carat, price)
print("=== Pearson ์๊ด๋ถ์ ===")
print(f"Hโ: ์บ๋ฟ๊ณผ ๊ฐ๊ฒฉ์ ์๊ด์ด ์๋ค (ฯ = 0)")
print(f"Hโ: ์บ๋ฟ๊ณผ ๊ฐ๊ฒฉ์ ์๊ด์ด ์๋ค (ฯ โ 0)")
print(f"\nPearson r: {r:.4f}")
print(f"p-value: {p_value:.6f}")
print(f"๊ฒฐ์ ๊ณ์ (Rยฒ): {r**2:.4f} โ ๊ฐ๊ฒฉ ๋ณ๋์ {r**2*100:.1f}%๋ฅผ ์บ๋ฟ์ผ๋ก ์ค๋ช
")
print(f"\n์๊ด ๊ฐ๋ ํด์:")
print(f" |r| < 0.3: ์ฝํ ์๊ด")
print(f" 0.3 โค |r| < 0.7: ์ค๊ฐ ์๊ด")
print(f" |r| โฅ 0.7: ๊ฐํ ์๊ด")
print(f"\nํ์ฌ |r| = {abs(r):.4f} โ ๊ฐํ ์์ ์๊ด (์บ๋ฟโ โ ๊ฐ๊ฒฉโ)")=== Pearson ์๊ด๋ถ์ === Hโ: ์บ๋ฟ๊ณผ ๊ฐ๊ฒฉ์ ์๊ด์ด ์๋ค (ฯ = 0) Hโ: ์บ๋ฟ๊ณผ ๊ฐ๊ฒฉ์ ์๊ด์ด ์๋ค (ฯ โ 0) Pearson r: 0.9209 p-value: 0.000000 ๊ฒฐ์ ๊ณ์ (Rยฒ): 0.8481 โ ๊ฐ๊ฒฉ ๋ณ๋์ 84.8%๋ฅผ ์บ๋ฟ์ผ๋ก ์ค๋ช ์๊ด ๊ฐ๋ ํด์: |r| < 0.3: ์ฝํ ์๊ด 0.3 โค |r| < 0.7: ์ค๊ฐ ์๊ด |r| โฅ 0.7: ๊ฐํ ์๊ด ํ์ฌ |r| = 0.9209 โ ๊ฐํ ์์ ์๊ด (์บ๋ฟโ โ ๊ฐ๊ฒฉโ)
Spearman ์์์๊ด๊ณ์
๐ ์ฌ์ฉ ์ํฉ ์์
- ์ค๋ฌธ์กฐ์ฌ: ๋ง์กฑ๋ ์์์ ์ฌ๊ตฌ๋งค ์ํฅ ์์์ ๊ด๊ณ
- ๊ฒฝ์ : GDP ์์์ ํ๋ณต์ง์ ์์์ ๊ด๊ณ
- ๊ต์ก: ํ์ ์ฑ์ ์์์ ์ทจ์ ๋ฅ ์์์ ๊ด๊ณ
- ์คํฌ์ธ : ์ฐ๋ด ์์์ ์ฑ์ ์์์ ๊ด๊ณ
๐ก ํต์ฌ ์ง๋ฌธ: โ๋ ๋ณ์ ๊ฐ์ ๋จ์กฐ์ (monotonic) ๊ด๊ณ๊ฐ ์๋๊ฐ?โ
โ ๏ธ Pearson์ ๋น๋ชจ์ ๋์: ์ ๊ท๋ถํฌ ๋ถํ์, ๋น์ ํ์ด์ง๋ง ๋จ์กฐ์ ์ธ ๊ด๊ณ๋ ๊ฐ์ง. ์: y = xยฒ (๋จ์กฐ ์ฆ๊ฐ ๊ตฌ๊ฐ์์๋ Spearman์ด ๋์)
# Tips: ์ด ๊ฒฐ์ ๊ธ์ก๊ณผ ํ์ ์์ ์๊ด
# ์ํฉ: ๊ฒฐ์ ๊ธ์ก์ด ๋์์๋ก ํ๋ ๋์ ๊ฒฝํฅ์ด ์๋์ง (์ ํํ ๋น๋ก๊ฐ ์๋๋๋ผ๋)
total_bill = tips['total_bill']
tip = tips['tip']
rho, p_value = spearmanr(total_bill, tip)
r_pearson, _ = pearsonr(total_bill, tip)
print("=== Spearman ์์ ์๊ด๋ถ์ ===")
print(f"Hโ: ๊ฒฐ์ ๊ธ์ก๊ณผ ํ์ ๋จ์กฐ์ ๊ด๊ณ๊ฐ ์๋ค")
print(f"Hโ: ๊ฒฐ์ ๊ธ์ก๊ณผ ํ์ ๋จ์กฐ์ ๊ด๊ณ๊ฐ ์๋ค")
print(f"\nSpearman ฯ: {rho:.4f}")
print(f"(๋น๊ต) Pearson r: {r_pearson:.4f}")
print(f"p-value: {p_value:.6f}")
print(f"\n๊ฒฐ๋ก : {'์ ์ํ ๋จ์กฐ ๊ด๊ณ - ๊ฒฐ์ ์ก์ด ๋์ผ๋ฉด ํ๋ ๋์ ๊ฒฝํฅ' if p_value < 0.05 else '๊ด๊ณ ์์'}")=== Spearman ์์ ์๊ด๋ถ์ === Hโ: ๊ฒฐ์ ๊ธ์ก๊ณผ ํ์ ๋จ์กฐ์ ๊ด๊ณ๊ฐ ์๋ค Hโ: ๊ฒฐ์ ๊ธ์ก๊ณผ ํ์ ๋จ์กฐ์ ๊ด๊ณ๊ฐ ์๋ค Spearman ฯ: 0.8264 (๋น๊ต) Pearson r: 0.6757 p-value: 0.000000 ๊ฒฐ๋ก : ์ ์ํ ๋จ์กฐ ๊ด๊ณ - ๊ฒฐ์ ์ก์ด ๋์ผ๋ฉด ํ๋ ๋์ ๊ฒฝํฅ
Kendallโs Tau
๐ ์ฌ์ฉ ์ํฉ ์์
- ์์ ๋ฐ์ดํฐ: ๋ ์ฌ์ฌ์์์ ์์ ํ๊ฐ ์ผ์น๋ (์: ๋ง์ง ์์)
- ์์ด ์ฒ๋: ๊ต์ก ์์ค(์ด์กธ/์ค์กธ/๊ณ ์กธ/๋์กธ)๊ณผ ์๋ ์์ค(ํ/์ค/์)์ ๊ด๊ณ
- ๋์์๊ฐ ๋ง์ ๋: 5์ ์ฒ๋ ์ค๋ฌธ์ฒ๋ผ ๊ฐ์ ๊ฐ์ด ๋ง์ ๋ฐ์ดํฐ
๐ก ํต์ฌ ์ง๋ฌธ: โ์์ ๋ฐ์ดํฐ์์ ๋ ๋ณ์์ **์ผ์น๋(concordance)**๋ ์ผ๋ง์ธ๊ฐ?โ
โ ๏ธ Spearman๋ณด๋ค ๋ณด์์ . ๋์์(ties)๊ฐ ๋ง์ ๋ ๋ ์ ํ.
# Titanic: ๊ฐ์ค ๋ฑ๊ธ๊ณผ ๋์ด์ ๊ด๊ณ
# ์ํฉ: 1๋ฑ์ ์น๊ฐ์ด ๋ ๋์ด๊ฐ ๋ง์ ๊ฒฝํฅ์ด ์๋์ง (์์ด vs ์ฐ์)
pclass = titanic['pclass'].dropna()
age = titanic['age'].dropna()
# ์ธ๋ฑ์ค ๋ง์ถ๊ธฐ
common_idx = pclass.index.intersection(age.index)
pclass_aligned = pclass.loc[common_idx]
age_aligned = age.loc[common_idx]
tau, p_value = kendalltau(pclass_aligned, age_aligned)
print("=== Kendall's Tau ์๊ด๋ถ์ ===")
print(f"Hโ: ๊ฐ์ค ๋ฑ๊ธ๊ณผ ๋์ด๋ ๊ด๊ณ๊ฐ ์๋ค")
print(f"Hโ: ๊ฐ์ค ๋ฑ๊ธ๊ณผ ๋์ด๋ ๊ด๊ณ๊ฐ ์๋ค")
print(f"\nKendall ฯ: {tau:.4f}")
print(f"p-value: {p_value:.4f}")
print(f"\n๊ฒฐ๋ก : {'์ ์ํ ๊ด๊ณ' if p_value < 0.05 else '๊ด๊ณ ์์'}")
if tau < 0:
print(f"ํด์: ฯ < 0 ์ด๋ฏ๋ก ๊ฐ์ค ๋ฑ๊ธโ(1๋ฑ๊ธ) โ ๋์ดโ (๊ณ ๊ธ ๊ฐ์ค์ ๋์ด ๋ง์ ์น๊ฐ)")=== Kendall's Tau ์๊ด๋ถ์ === Hโ: ๊ฐ์ค ๋ฑ๊ธ๊ณผ ๋์ด๋ ๊ด๊ณ๊ฐ ์๋ค Hโ: ๊ฐ์ค ๋ฑ๊ธ๊ณผ ๋์ด๋ ๊ด๊ณ๊ฐ ์๋ค Kendall ฯ: -0.1080 p-value: 0.0000 ๊ฒฐ๋ก : ์ ์ํ ๊ด๊ณ ํด์: ฯ < 0 ์ด๋ฏ๋ก ๊ฐ์ค ๋ฑ๊ธโ(1๋ฑ๊ธ) โ ๋์ดโ (๊ณ ๊ธ ๊ฐ์ค์ ๋์ด ๋ง์ ์น๊ฐ)
9. ํจ๊ณผ ํฌ๊ธฐ (Effect Size)
๐ฏ ์ ํจ๊ณผ ํฌ๊ธฐ๊ฐ ์ค์ํ๊ฐ์?
p-value๋ โ์ฐจ์ด๊ฐ ์๋๊ฐ?โ๋ง ์๋ ค์ฃผ๊ณ , โ์ผ๋ง๋ ํฐ ์ฐจ์ด์ธ๊ฐ?โ๋ ์๋ ค์ฃผ์ง ์์ต๋๋ค. ํ๋ณธ์ด ํฌ๋ฉด ์์ฃผ ์์ ์ฐจ์ด๋ ์ ์ํ๊ฒ ๋์ฌ ์ ์์ด์.
์์: 100๋ง ๋ช ๋์ A/B ํ ์คํธ์์ ์ ํ์จ 0.01%p ์ฐจ์ด๋ p < 0.05๊ฐ ๋ ์ ์์ง๋ง, ์ด ์ฐจ์ด๊ฐ ์ค์ ๋ก ๋น์ฆ๋์ค์ ์๋ฏธ ์๋ ์ฐจ์ด์ธ์ง๋ ํจ๊ณผ ํฌ๊ธฐ๋ก ํ๋จํด์ผ ํฉ๋๋ค.
Cohenโs d
๐ ์ฌ์ฉ ์ํฉ: ๋ ๊ทธ๋ฃน ํ๊ท ์ฐจ์ด์ ์ค์ง์ ์๋ฏธ ํด์
ํด์ ๊ธฐ์ค (Cohen, 1988):
- |d| < 0.2: ์์ ํจ๊ณผ (๋ฌด์ํด๋ ๋ ์์ค)
- 0.2 โค |d| < 0.5: ์ค๊ฐ ํจ๊ณผ
- 0.5 โค |d| < 0.8: ํฐ ํจ๊ณผ
- |d| โฅ 0.8: ๋งค์ฐ ํฐ ํจ๊ณผ
def cohens_d(group1, group2):
n1, n2 = len(group1), len(group2)
var1, var2 = group1.var(), group2.var()
pooled_std = np.sqrt(((n1-1)*var1 + (n2-1)*var2) / (n1+n2-2))
return (group1.mean() - group2.mean()) / pooled_std
# Titanic: ์์กด์ vs ์ฌ๋ง์ ๋์ด ์ฐจ์ด์ ํจ๊ณผ ํฌ๊ธฐ
# ์ํฉ: ๋์ด์ ์์กด์ ๊ด๊ณ. p-value๊ฐ ์ ์ํด๋ ์ค์ ๋ก ์๋ฏธ ์๋ ์ฐจ์ด์ธ์ง?
survived_ages = titanic[titanic['survived'] == 1]['age'].dropna()
died_ages = titanic[titanic['survived'] == 0]['age'].dropna()
d = cohens_d(survived_ages, died_ages)
t_stat, p_value = ttest_ind(survived_ages, died_ages)
print("=== ํจ๊ณผ ํฌ๊ธฐ ๋ถ์ ===")
print(f"์์กด์ ํ๊ท ๋์ด: {survived_ages.mean():.2f}์ธ")
print(f"์ฌ๋ง์ ํ๊ท ๋์ด: {died_ages.mean():.2f}์ธ")
print(f"์ฐจ์ด: {abs(survived_ages.mean() - died_ages.mean()):.2f}์ธ")
print(f"\nt-ํต๊ณ๋: {t_stat:.4f}")
print(f"p-value: {p_value:.4f} โ {'์ ์ํจ' if p_value < 0.05 else '์ ์ํ์ง ์์'}")
print(f"Cohen's d: {d:.4f}")
print(f"\nํจ๊ณผ ํฌ๊ธฐ ํด์:")
print(f" |d| < 0.2: ์์ ํจ๊ณผ")
print(f" 0.2 โค |d| < 0.5: ์ค๊ฐ ํจ๊ณผ")
print(f" 0.5 โค |d| < 0.8: ํฐ ํจ๊ณผ")
print(f" |d| โฅ 0.8: ๋งค์ฐ ํฐ ํจ๊ณผ")
print(f"\nํ์ฌ: |d| = {abs(d):.4f} โ ", end="")
if abs(d) >= 0.8:
print("๋งค์ฐ ํฐ ํจ๊ณผ")
elif abs(d) >= 0.5:
print("ํฐ ํจ๊ณผ")
elif abs(d) >= 0.2:
print("์ค๊ฐ ํจ๊ณผ")
else:
print("์์ ํจ๊ณผ โ ํต๊ณ์ ์ผ๋ก ์ ์ํ์ง๋ง ์ค์ง์ ์๋ฏธ๋ ์ ํ์ !")=== ํจ๊ณผ ํฌ๊ธฐ ๋ถ์ === ์์กด์ ํ๊ท ๋์ด: 28.34์ธ ์ฌ๋ง์ ํ๊ท ๋์ด: 30.63์ธ ์ฐจ์ด: 2.29์ธ t-ํต๊ณ๋: -2.0551 p-value: 0.0402 โ ์ ์ํจ Cohen's d: -0.1616 ํจ๊ณผ ํฌ๊ธฐ ํด์: |d| < 0.2: ์์ ํจ๊ณผ 0.2 โค |d| < 0.5: ์ค๊ฐ ํจ๊ณผ 0.5 โค |d| < 0.8: ํฐ ํจ๊ณผ |d| โฅ 0.8: ๋งค์ฐ ํฐ ํจ๊ณผ ํ์ฌ: |d| = 0.1616 โ ์์ ํจ๊ณผ โ ํต๊ณ์ ์ผ๋ก ์ ์ํ์ง๋ง ์ค์ง์ ์๋ฏธ๋ ์ ํ์ !
Cramรฉrโs V
๐ ์ฌ์ฉ ์ํฉ: ๋ฒ์ฃผํ ๋ณ์ ๊ฐ ์ฐ๊ด์ฑ ๊ฐ๋ ์ธก์ (์นด์ด์ ๊ณฑ ๊ฒ์ ํ)
ํด์ ๊ธฐ์ค:
- V < 0.1: ๋ฌด์ํ ์์ค
- 0.1 โค V < 0.3: ์ฝํ ์ฐ๊ด
- 0.3 โค V < 0.5: ์ค๊ฐ ์ฐ๊ด
- V โฅ 0.5: ๊ฐํ ์ฐ๊ด
def cramers_v(contingency_table):
chi2 = chi2_contingency(contingency_table)[0]
n = contingency_table.sum().sum()
min_dim = min(contingency_table.shape) - 1
return np.sqrt(chi2 / (n * min_dim))
# Titanic: ์ฑ๋ณ๊ณผ ์์กด์ ์ฐ๊ด ๊ฐ๋
contingency = pd.crosstab(titanic['sex'], titanic['survived'])
v = cramers_v(contingency)
chi2, p_value, _, _ = chi2_contingency(contingency)
print("=== Cramรฉr's V (์ฐ๊ด์ฑ ๊ฐ๋) ===")
print(f"ฯยฒ = {chi2:.4f}, p-value = {p_value:.6f}")
print(f"Cramรฉr's V = {v:.4f}")
print(f"\nํด์ ๊ธฐ์ค:")
print(f" V < 0.1: ๋ฌด์ํ ์์ค")
print(f" 0.1 โค V < 0.3: ์ฝํ ์ฐ๊ด")
print(f" 0.3 โค V < 0.5: ์ค๊ฐ ์ฐ๊ด")
print(f" V โฅ 0.5: ๊ฐํ ์ฐ๊ด")
print(f"\nํ์ฌ: V = {v:.4f} โ ", end="")
if v >= 0.5:
print("๊ฐํ ์ฐ๊ด โ ์ฑ๋ณ์ด ์์กด์ ๋งค์ฐ ํฐ ์ํฅ!")
elif v >= 0.3:
print("์ค๊ฐ ์ฐ๊ด")
elif v >= 0.1:
print("์ฝํ ์ฐ๊ด")
else:
print("๋ฌด์ํ ์์ค")=== Cramรฉr's V (์ฐ๊ด์ฑ ๊ฐ๋) === ฯยฒ = 260.7170, p-value = 0.000000 Cramรฉr's V = 0.5410 ํด์ ๊ธฐ์ค: V < 0.1: ๋ฌด์ํ ์์ค 0.1 โค V < 0.3: ์ฝํ ์ฐ๊ด 0.3 โค V < 0.5: ์ค๊ฐ ์ฐ๊ด V โฅ 0.5: ๊ฐํ ์ฐ๊ด ํ์ฌ: V = 0.5410 โ ๊ฐํ ์ฐ๊ด โ ์ฑ๋ณ์ด ์์กด์ ๋งค์ฐ ํฐ ์ํฅ!
10. ๋ค์ค๊ฒ์ ๋ณด์ (Multiple Testing Correction)
๐ฏ ์ ๋ณด์ ์ด ํ์ํ๊ฐ์?
์ฌ๋ฌ ๊ฒ์ ์ ๋์์ ์ํํ๋ฉด **1์ข ์ค๋ฅ(๊ฑฐ์ง ์์ฑ)**๊ฐ ๋์ ๋ฉ๋๋ค.
์์: ฮฑ = 0.05๋ก 20๊ฐ ๊ฒ์ ์ ํ๋ฉด
- ์ ์ด๋ 1๊ฐ ๊ฑฐ์ง ์์ฑ ํ๋ฅ = 1 - (0.95)^20 = 64%!
์ด๋ฅผ **๋ค์ค ๋น๊ต ๋ฌธ์ (Multiple Comparison Problem)**๋ผ๊ณ ํฉ๋๋ค.
Bonferroni Correction
๐ ์ฌ์ฉ ์ํฉ:
- ์ฌ๋ฌ A/B ํ ์คํธ๋ฅผ ๋์์ ๋ถ์ํ ๋
- ANOVA ํ ์ฌํ๊ฒ์ ์์ ์ฌ๋ฌ ์์ ๋น๊ตํ ๋
- ์ ์ ์ฒด ์ฐ๊ตฌ์์ ์์ฒ ๊ฐ์ ์ ์ ์๋ฅผ ๊ฒ์ ํ ๋
๐ก ๋ฐฉ๋ฒ: ฮฑ๋ฅผ ๊ฒ์ ํ์(k)๋ก ๋๋. ์: 5๊ฐ ๊ฒ์ ์ 0.05/5 = 0.01
โ ๏ธ ๋งค์ฐ ๋ณด์์ . ์ค์ ํจ๊ณผ๋ ๋์น ์ ์์ (2์ข ์ค๋ฅ ์ฆ๊ฐ)
from statsmodels.stats.multitest import multipletests
# ์ฌ๋ฌ A/B ํ
์คํธ ๊ฒฐ๊ณผ
# ์ํฉ: 5๊ฐ์ง UI ์์๋ฅผ ๋์์ ํ
์คํธ. ์ด๋ค ๊ฒ์ด ์ง์ง ํจ๊ณผ๊ฐ ์๋์ง?
p_values = [0.03, 0.04, 0.01, 0.08, 0.002]
test_names = ['๋ฒํผ ์์', 'ํค๋๋ผ์ธ', 'CTA ์์น', '์ด๋ฏธ์ง', '๊ฐ๊ฒฉ ํ์']
# Bonferroni ๋ณด์
rejected, corrected_p, _, _ = multipletests(p_values, method='bonferroni')
print("=== ๋ค์ค๊ฒ์ ๋ณด์ (Bonferroni) ===")
print(f"๊ฒ์ ์: {len(p_values)}")
print(f"๋ณด์ ๋ ์ ์์์ค: 0.05 / {len(p_values)} = {0.05/len(p_values):.3f}")
print(f"\n{'ํ
์คํธ':<12} {'์๋ p-value':<15} {'๋ณด์ p-value':<15} {'๊ฒฐ๋ก '}")
print("-" * 60)
for name, p, cp, rej in zip(test_names, p_values, corrected_p, rejected):
result = "โ ์ ์ํจ" if rej else "โ ์ ์ํ์ง ์์"
print(f"{name:<12} {p:<15.4f} {min(cp, 1.0):<15.4f} {result}")=== ๋ค์ค๊ฒ์ ๋ณด์ (Bonferroni) === ๊ฒ์ ์: 5 ๋ณด์ ๋ ์ ์์์ค: 0.05 / 5 = 0.010 ํ ์คํธ ์๋ p-value ๋ณด์ p-value ๊ฒฐ๋ก ------------------------------------------------------------ ๋ฒํผ ์์ 0.0300 0.1500 โ ์ ์ํ์ง ์์ ํค๋๋ผ์ธ 0.0400 0.2000 โ ์ ์ํ์ง ์์ CTA ์์น 0.0100 0.0500 โ ์ ์ํ์ง ์์ ์ด๋ฏธ์ง 0.0800 0.4000 โ ์ ์ํ์ง ์์ ๊ฐ๊ฒฉ ํ์ 0.0020 0.0100 โ ์ ์ํจ
Benjamini-Hochberg (FDR)
๐ ์ฌ์ฉ ์ํฉ:
- ๋ง์ ๊ฒ์ ์ ์ํํ์ง๋ง ์ผ๋ถ ๊ฑฐ์ง ์์ฑ์ ๊ฐ์ํ ์ ์์ ๋
- ํ์์ ๋ถ์์์ ํ๋ณด๋ฅผ ์ ๋ณํ ๋
- ์ ์ ์ฒด ์ฐ๊ตฌ์์ Bonferroni๊ฐ ๋๋ฌด ๋ณด์์ ์ผ ๋
๐ก ๋ฐฉ๋ฒ: False Discovery Rate (FDR)๋ฅผ ์ ์ด. โ์ ์ํ๋ค๊ณ ํ์ ํ ๊ฒ ์ค ๊ฑฐ์ง ์์ฑ ๋น์จโ์ 5%๋ก ์ ์ด
# Benjamini-Hochberg ๋ณด์
rejected_bh, corrected_p_bh, _, _ = multipletests(p_values, method='fdr_bh')
print("=== ๋ค์ค๊ฒ์ ๋ณด์ (Benjamini-Hochberg FDR) ===")
print(f"\n{'ํ
์คํธ':<12} {'์๋ p-value':<15} {'๋ณด์ p-value':<15} {'๊ฒฐ๋ก '}")
print("-" * 60)
for name, p, cp, rej in zip(test_names, p_values, corrected_p_bh, rejected_bh):
result = "โ ์ ์ํจ" if rej else "โ ์ ์ํ์ง ์์"
print(f"{name:<12} {p:<15.4f} {cp:<15.4f} {result}")
print(f"\n๋น๊ต:")
print(f" Bonferroni๋ก ์ ์: {sum(rejected)}๊ฐ")
print(f" FDR(BH)๋ก ์ ์: {sum(rejected_bh)}๊ฐ")
print(f"\nโ FDR์ด ๋ ๋ณด์์ ์ด์ด์ ๋ ๋ง์ ๋ฐ๊ฒฌ ๊ฐ๋ฅ")
print(f" ๋จ, ์ด ์ค ์ฝ 5%๋ ๊ฑฐ์ง ์์ฑ์ผ ์ ์์")=== ๋ค์ค๊ฒ์ ๋ณด์ (Benjamini-Hochberg FDR) === ํ ์คํธ ์๋ p-value ๋ณด์ p-value ๊ฒฐ๋ก ------------------------------------------------------------ ๋ฒํผ ์์ 0.0300 0.0500 โ ์ ์ํจ ํค๋๋ผ์ธ 0.0400 0.0500 โ ์ ์ํจ CTA ์์น 0.0100 0.0250 โ ์ ์ํจ ์ด๋ฏธ์ง 0.0800 0.0800 โ ์ ์ํ์ง ์์ ๊ฐ๊ฒฉ ํ์ 0.0020 0.0100 โ ์ ์ํจ ๋น๊ต: Bonferroni๋ก ์ ์: 1๊ฐ FDR(BH)๋ก ์ ์: 4๊ฐ โ FDR์ด ๋ ๋ณด์์ ์ด์ด์ ๋ ๋ง์ ๋ฐ๊ฒฌ ๊ฐ๋ฅ ๋จ, ์ด ์ค ์ฝ 5%๋ ๊ฑฐ์ง ์์ฑ์ผ ์ ์์
11. ๊ฒ์ ๋ ฅ ๋ถ์ (Power Analysis)
๐ฏ ์ธ์ ์ฌ์ฉํ๋์?
์คํ ์ค๊ณ ๋จ๊ณ์์ โ๋ช ๋ช ์ ํ๋ณธ์ด ํ์ํ๊ฐ?โ๋ฅผ ๊ณ์ฐํ ๋ ์ฌ์ฉํฉ๋๋ค.
ํ๋ณธ์ด ๋๋ฌด ์ ์ผ๋ฉด ์ค์ ํจ๊ณผ๊ฐ ์์ด๋ ํ์ง ๋ชปํ๊ณ (2์ข ์ค๋ฅ), ํ๋ณธ์ด ๋๋ฌด ๋ง์ผ๋ฉด ์์ ๋ญ๋น์ ๋๋ค.
from statsmodels.stats.power import TTestIndPower
power_analysis = TTestIndPower()
# ์๋๋ฆฌ์ค: ํจ๊ณผ ํฌ๊ธฐ 0.3, ๊ฒ์ ๋ ฅ 80%, ์ ์์์ค 5%
# ์ํฉ: "์ UI๊ฐ ์ ํ์จ์ ์ค๊ฐ ์ ๋(d=0.3) ๋์ผ ๊ฒ์ผ๋ก ์์.
# 80% ํ๋ฅ ๋ก ์ด ํจ๊ณผ๋ฅผ ํ์งํ๋ ค๋ฉด ๋ช ๋ช
์ด ํ์ํ๊ฐ?"
effect_size = 0.3
alpha = 0.05
power = 0.8
n = power_analysis.solve_power(effect_size=effect_size,
alpha=alpha,
power=power,
ratio=1.0,
alternative='two-sided')
print("=== ํ๋ณธ ํฌ๊ธฐ ๊ณ์ฐ ===")
print(f"๋ชฉํ ํจ๊ณผ ํฌ๊ธฐ (Cohen's d): {effect_size} (์ค๊ฐ ํจ๊ณผ)")
print(f"์ ์์์ค (ฮฑ): {alpha}")
print(f"๋ชฉํ ๊ฒ์ ๋ ฅ (1-ฮฒ): {power} (80% ํ๋ฅ ๋ก ํจ๊ณผ ํ์ง)")
print(f"\nํ์ ํ๋ณธ ํฌ๊ธฐ: ๊ทธ๋ฃน๋น {n:.0f}๋ช
")
print(f"์ด ํ์ ์ธ์: {n*2:.0f}๋ช
")
# ๋ค์ํ ํจ๊ณผ ํฌ๊ธฐ์ ๋ฐ๋ฅธ ํ์ ํ๋ณธ ์
print("\nํจ๊ณผ ํฌ๊ธฐ๋ณ ํ์ ํ๋ณธ ์ (๊ฒ์ ๋ ฅ 80%):")
for es, desc in [(0.2, '์์ ํจ๊ณผ'), (0.3, '์ค๊ฐ ํจ๊ณผ'), (0.5, 'ํฐ ํจ๊ณผ'), (0.8, '๋งค์ฐ ํฐ ํจ๊ณผ')]:
n = power_analysis.solve_power(effect_size=es, alpha=0.05, power=0.8, ratio=1.0)
print(f" d = {es} ({desc}): ๊ทธ๋ฃน๋น {n:.0f}๋ช
(์ด {n*2:.0f}๋ช
)")=== ํ๋ณธ ํฌ๊ธฐ ๊ณ์ฐ === ๋ชฉํ ํจ๊ณผ ํฌ๊ธฐ (Cohen's d): 0.3 (์ค๊ฐ ํจ๊ณผ) ์ ์์์ค (ฮฑ): 0.05 ๋ชฉํ ๊ฒ์ ๋ ฅ (1-ฮฒ): 0.8 (80% ํ๋ฅ ๋ก ํจ๊ณผ ํ์ง) ํ์ ํ๋ณธ ํฌ๊ธฐ: ๊ทธ๋ฃน๋น 176๋ช ์ด ํ์ ์ธ์: 352๋ช ํจ๊ณผ ํฌ๊ธฐ๋ณ ํ์ ํ๋ณธ ์ (๊ฒ์ ๋ ฅ 80%): d = 0.2 (์์ ํจ๊ณผ): ๊ทธ๋ฃน๋น 394๋ช (์ด 787๋ช ) d = 0.3 (์ค๊ฐ ํจ๊ณผ): ๊ทธ๋ฃน๋น 176๋ช (์ด 352๋ช ) d = 0.5 (ํฐ ํจ๊ณผ): ๊ทธ๋ฃน๋น 64๋ช (์ด 128๋ช ) d = 0.8 (๋งค์ฐ ํฐ ํจ๊ณผ): ๊ทธ๋ฃน๋น 26๋ช (์ด 51๋ช )
12. ๊ฒ์ ์ ํ ์์ฝํ
๋ฐ์ดํฐ ์ ํ๋ณ ๊ฒ์ ์ ํ
| ์ํฉ | ๋ชจ์์ ๊ฒ์ | ๋น๋ชจ์์ ๊ฒ์ |
|---|---|---|
| 1๊ฐ ํ๋ณธ ํ๊ท vs ๊ธฐ์ค๊ฐ | One-sample t-test | Wilcoxon signed-rank |
| 2๊ฐ ๋ ๋ฆฝ ํ๋ณธ ๋น๊ต | Independent t-test | Mann-Whitney U |
| 2๊ฐ ๋์ ํ๋ณธ ๋น๊ต | Paired t-test | Wilcoxon signed-rank |
| 3๊ฐ+ ๋ ๋ฆฝ ํ๋ณธ ๋น๊ต | One-way ANOVA | Kruskal-Wallis H |
| 2ร2 ๋ฒ์ฃผ (์ํ๋ณธ) | - | Fisherโs exact |
| ๋ฒ์ฃผ ๋ ๋ฆฝ์ฑ | - | Chi-square |
| ๋์ ๋ฒ์ฃผ ์ ํ ๋น๊ต | - | McNemarโs |
| ์๊ด๊ด๊ณ | Pearson r | Spearman ฯ, Kendall ฯ |
์ํฉ๋ณ ๋น ๋ฅธ ๊ฐ์ด๋
Q: ๋ ๊ทธ๋ฃน ํ๊ท ๋น๊ต?
โโโ ๊ฐ์ ๋์์ ์ ํ? โ ๋์ํ๋ณธ t-test (์ ๊ท) / Wilcoxon (๋น์ ๊ท)
โโโ ๋ค๋ฅธ ๋์? โ ๋
๋ฆฝํ๋ณธ t-test (์ ๊ท) / Mann-Whitney (๋น์ ๊ท)
Q: 3๊ฐ ์ด์ ๊ทธ๋ฃน ๋น๊ต?
โโโ ์ ๊ท๋ถํฌ? โ One-way ANOVA
โโโ ๋น์ ๊ท๋ถํฌ? โ Kruskal-Wallis
Q: ๋ ๋ฒ์ฃผํ ๋ณ์ ๊ด๊ณ?
โโโ ๊ธฐ๋๋น๋ < 5 ์์? โ Fisher's exact
โโโ ๊ธฐ๋๋น๋ โฅ 5? โ Chi-square
Q: ๋ ์ฐ์ํ ๋ณ์ ๊ด๊ณ?
โโโ ์ ํ ๊ด๊ณ? โ Pearson r
โโโ ๋จ์กฐ ๊ด๊ณ? โ Spearman ฯํด์ฆ
๋ฌธ์ 1
Titanic ๋ฐ์ดํฐ์์ ๊ฐ์ค ๋ฑ๊ธ(pclass)์ ๋ฐ๋ผ ์์กด์จ์ ์ ์ํ ์ฐจ์ด๊ฐ ์๋์ง ์ ์ ํ ๊ฒ์ ์ ์ํํ์ธ์.
์ ๋ต ๋ณด๊ธฐ
# ๋ฒ์ฃผํ vs ๋ฒ์ฃผํ โ ์นด์ด์ ๊ณฑ ๊ฒ์
contingency = pd.crosstab(titanic['pclass'], titanic['survived'])
chi2, p_value, dof, expected = chi2_contingency(contingency)
print("๊ต์ฐจํ:")
print(contingency)
print(f"\nฯยฒ = {chi2:.4f}, p-value = {p_value:.6f}")
print(f"\n๊ฒฐ๋ก : {'๊ฐ์ค ๋ฑ๊ธ๊ณผ ์์กด์จ์ ์ฐ๊ด๋จ' if p_value < 0.05 else '์ฐ๊ด ์์'}")
# ํจ๊ณผ ํฌ๊ธฐ
v = cramers_v(contingency)
print(f"Cramรฉr's V = {v:.4f} (์ค๊ฐ ์ ๋์ ์ฐ๊ด)")๋ฌธ์ 2
Tips ๋ฐ์ดํฐ์์ ํก์ฐ์์ ๋นํก์ฐ์์ ํ ๊ธ์ก ๋ถํฌ๊ฐ ๋ค๋ฅธ์ง ์ ์ ํ ๊ฒ์ ์ผ๋ก ํ์ธํ์ธ์.
์ ๋ต ๋ณด๊ธฐ
smoker_tip = tips[tips['smoker'] == 'Yes']['tip']
nonsmoker_tip = tips[tips['smoker'] == 'No']['tip']
# ์ ๊ท์ฑ ๊ฒ์
_, p_smoker = shapiro(smoker_tip)
_, p_nonsmoker = shapiro(nonsmoker_tip)
print(f"์ ๊ท์ฑ (ํก์ฐ): p = {p_smoker:.4f}")
print(f"์ ๊ท์ฑ (๋นํก์ฐ): p = {p_nonsmoker:.4f}")
# ์ ๊ท์ฑ ๋ถ์ถฉ์กฑ โ Mann-Whitney U ์ฌ์ฉ
stat, p_value = mannwhitneyu(smoker_tip, nonsmoker_tip)
print(f"\nMann-Whitney U: {stat:.2f}")
print(f"p-value: {p_value:.4f}")
print(f"\n๊ฒฐ๋ก : {'ํ ๋ถํฌ๊ฐ ๋ค๋ฆ' if p_value < 0.05 else 'ํ ๋ถํฌ ์ฐจ์ด ์์'}")๋ค์ ๋จ๊ณ
- ํ๊ท๋ถ์์์ ๋ณ์ ๊ฐ ๊ด๊ณ๋ฅผ ๋ชจ๋ธ๋งํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
- A/B ํ ์คํธ์์ ์คํ ์ค๊ณ๋ฅผ ๋ฐฐ์๋ณด์ธ์.