13. ์ ๋ขฐ๊ตฌ๊ฐ (Confidence Intervals)
13. ์ ๋ขฐ๊ตฌ๊ฐ (Confidence Intervals)¶
์ด์ : ํ๋ณธ๊ณผ ์ถ์ | ๋ค์: ๊ฐ์ค๊ฒ์ ์ฌํ
๊ฐ์¶
์ ๋ขฐ๊ตฌ๊ฐ(Confidence Interval, CI)์ ๋ชจ์๊ฐ ํฌํจ๋ ๊ฒ์ผ๋ก ๊ธฐ๋๋๋ ๊ตฌ๊ฐ์ ์ ๊ณตํฉ๋๋ค. ์ ์ถ์ ์ด "๋จ์ผ ๊ฐ"์ ์ ๊ณตํ๋ค๋ฉด, ๊ตฌ๊ฐ์ถ์ ์ "๋ถํ์ค์ฑ์ ๋ฒ์"๋ฅผ ํจ๊ป ์ ๊ณตํฉ๋๋ค.
1. ๊ตฌ๊ฐ์ถ์ ์ ๊ธฐ๋ณธ ๊ฐ๋ ¶
1.1 ์ ๋ขฐ๊ตฌ๊ฐ์ ์ ์¶
import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
import seaborn as sns
# ์ ๋ขฐ๊ตฌ๊ฐ์ ์ฌ๋ฐ๋ฅธ ํด์
# "95% ์ ๋ขฐ๊ตฌ๊ฐ"์ ์๋ฏธ:
# - ๋์ผํ ๋ฐฉ๋ฒ์ผ๋ก 100๋ฒ ํ๋ณธ์ถ์ถํ์ฌ ๊ตฌ๊ฐ์ ๊ตฌํ๋ฉด,
# - ์ฝ 95๋ฒ์ ๋ชจ์๋ฅผ ํฌํจํ ๊ฒ์ด๋ค
# ์๋ฎฌ๋ ์ด์
์ผ๋ก ์ดํดํ๊ธฐ
np.random.seed(42)
# ๋ชจ์ ์ค์ (์ค์ ๋ก๋ ๋ฏธ์ง)
mu = 100
sigma = 15
n = 30
confidence_level = 0.95
# 100๋ฒ ํ๋ณธ ์ถ์ถํ์ฌ ์ ๋ขฐ๊ตฌ๊ฐ ๊ณ์ฐ
n_simulations = 100
intervals = []
contains_mu = 0
z_critical = stats.norm.ppf((1 + confidence_level) / 2)
for i in range(n_simulations):
sample = np.random.normal(mu, sigma, n)
x_bar = sample.mean()
se = sigma / np.sqrt(n) # ๋ชจํ์คํธ์ฐจ๋ฅผ ์๋ค๊ณ ๊ฐ์
lower = x_bar - z_critical * se
upper = x_bar + z_critical * se
intervals.append((lower, upper))
if lower <= mu <= upper:
contains_mu += 1
print(f"100๊ฐ์ 95% ์ ๋ขฐ๊ตฌ๊ฐ ์ค ๋ชจํ๊ท ์ ํฌํจํ๋ ๊ตฌ๊ฐ: {contains_mu}๊ฐ")
# ์๊ฐํ (์ฒซ 20๊ฐ ๊ตฌ๊ฐ)
fig, ax = plt.subplots(figsize=(12, 8))
for i in range(20):
lower, upper = intervals[i]
color = 'blue' if lower <= mu <= upper else 'red'
ax.plot([lower, upper], [i, i], color=color, linewidth=2)
ax.scatter([(lower + upper)/2], [i], color=color, s=30)
ax.axvline(mu, color='green', linestyle='--', linewidth=2, label=f'ฮผ = {mu}')
ax.set_xlabel('๊ฐ')
ax.set_ylabel('์๋ฎฌ๋ ์ด์
๋ฒํธ')
ax.set_title('์ ๋ขฐ๊ตฌ๊ฐ์ ์๋ฏธ: 20๊ฐ ์ค ๋ชจํ๊ท ์ ํฌํจํ์ง ์๋ ๊ตฌ๊ฐ(๋นจ๊ฐ)')
ax.legend()
ax.set_xlim(90, 110)
plt.show()
1.2 ์ ๋ขฐ์์ค๊ณผ ๊ตฌ๊ฐ ํญ์ ๊ด๊ณ¶
# ์ ๋ขฐ์์ค โ โ ๊ตฌ๊ฐ ํญ โ
# ํ๋ณธ ํฌ๊ธฐ โ โ ๊ตฌ๊ฐ ํญ โ
np.random.seed(42)
sample = np.random.normal(100, 15, 50)
x_bar = sample.mean()
s = sample.std(ddof=1)
n = len(sample)
se = s / np.sqrt(n)
confidence_levels = [0.80, 0.90, 0.95, 0.99]
print("์ ๋ขฐ์์ค์ ๋ฐ๋ฅธ ์ ๋ขฐ๊ตฌ๊ฐ:")
print("-" * 60)
print(f"ํ๋ณธํ๊ท = {x_bar:.2f}, ํ์ค์ค์ฐจ = {se:.2f}")
print("-" * 60)
for cl in confidence_levels:
t_critical = stats.t.ppf((1 + cl) / 2, df=n-1)
margin = t_critical * se
lower = x_bar - margin
upper = x_bar + margin
width = upper - lower
print(f"{int(cl*100)}% CI: [{lower:.2f}, {upper:.2f}], ํญ = {width:.2f}")
# ์๊ฐํ
fig, ax = plt.subplots(figsize=(10, 5))
colors = plt.cm.viridis(np.linspace(0.2, 0.8, len(confidence_levels)))
for i, (cl, color) in enumerate(zip(confidence_levels, colors)):
t_critical = stats.t.ppf((1 + cl) / 2, df=n-1)
margin = t_critical * se
ax.barh(i, 2*margin, left=x_bar-margin, height=0.6, color=color,
label=f'{int(cl*100)}% CI', alpha=0.7)
ax.axvline(x_bar, color='red', linestyle='-', linewidth=2, label='ํ๋ณธํ๊ท ')
ax.set_yticks(range(len(confidence_levels)))
ax.set_yticklabels([f'{int(cl*100)}%' for cl in confidence_levels])
ax.set_xlabel('๊ฐ')
ax.set_ylabel('์ ๋ขฐ์์ค')
ax.set_title('์ ๋ขฐ์์ค๊ณผ ๊ตฌ๊ฐ ํญ์ ๊ด๊ณ')
ax.legend(loc='upper right')
plt.show()
2. ๋ชจํ๊ท ์ ์ ๋ขฐ๊ตฌ๊ฐ¶
2.1 ๋ชจ๋ถ์ฐ์ด ์๋ ค์ง ๊ฒฝ์ฐ (Z-๊ตฌ๊ฐ)¶
def ci_mean_z(sample, sigma, confidence=0.95):
"""๋ชจ๋ถ์ฐ์ด ์๋ ค์ง ๊ฒฝ์ฐ ๋ชจํ๊ท ์ ์ ๋ขฐ๊ตฌ๊ฐ (Z-๊ตฌ๊ฐ)"""
n = len(sample)
x_bar = np.mean(sample)
se = sigma / np.sqrt(n)
z_critical = stats.norm.ppf((1 + confidence) / 2)
margin = z_critical * se
return x_bar - margin, x_bar + margin, margin
# ์์: ์ ์กฐ ๊ณต์ ์ ๋ถ๋๋ฅ ๋ชจ๋ํฐ๋ง
# ๊ณผ๊ฑฐ ๋ฐ์ดํฐ๋ก ฯ = 2.5์์ ์๊ณ ์์
np.random.seed(42)
sigma_known = 2.5
sample = np.random.normal(50, sigma_known, 40)
lower, upper, margin = ci_mean_z(sample, sigma_known, 0.95)
print("๋ชจ๋ถ์ฐ์ด ์๋ ค์ง ๊ฒฝ์ฐ (Z-๊ตฌ๊ฐ):")
print(f"ํ๋ณธํ๊ท : {sample.mean():.3f}")
print(f"95% ์ ๋ขฐ๊ตฌ๊ฐ: [{lower:.3f}, {upper:.3f}]")
print(f"์ค์ฐจ ํ๊ณ: ยฑ{margin:.3f}")
2.2 ๋ชจ๋ถ์ฐ์ด ์๋ ค์ง์ง ์์ ๊ฒฝ์ฐ (t-๊ตฌ๊ฐ)¶
def ci_mean_t(sample, confidence=0.95):
"""๋ชจ๋ถ์ฐ์ด ๋ฏธ์ง์ธ ๊ฒฝ์ฐ ๋ชจํ๊ท ์ ์ ๋ขฐ๊ตฌ๊ฐ (t-๊ตฌ๊ฐ)"""
n = len(sample)
x_bar = np.mean(sample)
s = np.std(sample, ddof=1)
se = s / np.sqrt(n)
t_critical = stats.t.ppf((1 + confidence) / 2, df=n-1)
margin = t_critical * se
return x_bar - margin, x_bar + margin, margin
# ์์
np.random.seed(42)
sample = np.random.normal(50, 2.5, 40)
lower_t, upper_t, margin_t = ci_mean_t(sample, 0.95)
print("๋ชจ๋ถ์ฐ์ด ๋ฏธ์ง์ธ ๊ฒฝ์ฐ (t-๊ตฌ๊ฐ):")
print(f"ํ๋ณธํ๊ท : {sample.mean():.3f}")
print(f"ํ๋ณธํ์คํธ์ฐจ: {sample.std(ddof=1):.3f}")
print(f"95% ์ ๋ขฐ๊ตฌ๊ฐ: [{lower_t:.3f}, {upper_t:.3f}]")
print(f"์ค์ฐจ ํ๊ณ: ยฑ{margin_t:.3f}")
# scipy.stats ํ์ฉ
sem = stats.sem(sample) # ํ์ค์ค์ฐจ
ci_scipy = stats.t.interval(0.95, df=len(sample)-1, loc=sample.mean(), scale=sem)
print(f"\nscipy ๊ฒฐ๊ณผ: [{ci_scipy[0]:.3f}, {ci_scipy[1]:.3f}]")
2.3 t-๋ถํฌ ์ดํด¶
# t-๋ถํฌ: ํ๋ณธ ํฌ๊ธฐ๊ฐ ์์ ๋ ์ฌ์ฉ
# t = (Xฬ - ฮผ) / (S/โn) ~ t(n-1)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# t-๋ถํฌ vs ์ ๊ท๋ถํฌ
x = np.linspace(-4, 4, 200)
axes[0].plot(x, stats.norm.pdf(x), 'b-', lw=2, label='N(0,1)')
dfs = [3, 10, 30]
colors = ['red', 'green', 'purple']
for df, color in zip(dfs, colors):
axes[0].plot(x, stats.t.pdf(x, df), linestyle='--', lw=2,
color=color, label=f't(df={df})')
axes[0].set_xlabel('x')
axes[0].set_ylabel('๋ฐ๋')
axes[0].set_title('t-๋ถํฌ vs ํ์ค์ ๊ท๋ถํฌ')
axes[0].legend()
axes[0].grid(alpha=0.3)
# ์์ ๋์ ๋ฐ๋ฅธ ์๊ณ๊ฐ ๋ณํ
dfs_range = np.arange(2, 101)
t_criticals = [stats.t.ppf(0.975, df) for df in dfs_range]
z_critical = stats.norm.ppf(0.975)
axes[1].plot(dfs_range, t_criticals, 'b-', lw=2, label='t(0.975, df)')
axes[1].axhline(z_critical, color='r', linestyle='--', lw=2,
label=f'z(0.975) = {z_critical:.3f}')
axes[1].set_xlabel('์์ ๋ (df)')
axes[1].set_ylabel('์๊ณ๊ฐ')
axes[1].set_title('์์ ๋์ ๋ฐ๋ฅธ 95% ์๊ณ๊ฐ')
axes[1].legend()
axes[1].grid(alpha=0.3)
plt.tight_layout()
plt.show()
# ํ๋ก ์ ๋ฆฌ
print("์์ ๋์ ๋ฐ๋ฅธ 95% ์ ๋ขฐ๊ตฌ๊ฐ ์๊ณ๊ฐ:")
print("-" * 30)
for df in [5, 10, 20, 30, 50, 100, np.inf]:
if df == np.inf:
t_val = stats.norm.ppf(0.975)
print(f"df = โ (์ ๊ท): t = {t_val:.4f}")
else:
t_val = stats.t.ppf(0.975, df)
print(f"df = {df:3.0f}: t = {t_val:.4f}")
3. ๋ชจ๋น์จ์ ์ ๋ขฐ๊ตฌ๊ฐ¶
3.1 ์ ๊ท๊ทผ์ฌ๋ฅผ ์ด์ฉํ ์ ๋ขฐ๊ตฌ๊ฐ¶
def ci_proportion(successes, n, confidence=0.95):
"""๋ชจ๋น์จ์ ์ ๋ขฐ๊ตฌ๊ฐ (์ ๊ท๊ทผ์ฌ)"""
p_hat = successes / n
se = np.sqrt(p_hat * (1 - p_hat) / n)
z_critical = stats.norm.ppf((1 + confidence) / 2)
margin = z_critical * se
return p_hat - margin, p_hat + margin
# ์์: ์ฌ๋ก ์กฐ์ฌ
# 1000๋ช
์ค 420๋ช
์ด ์ฐฌ์ฑ
successes = 420
n = 1000
lower, upper = ci_proportion(successes, n, 0.95)
print("๋ชจ๋น์จ์ 95% ์ ๋ขฐ๊ตฌ๊ฐ (์ ๊ท๊ทผ์ฌ):")
print(f"ํ๋ณธ๋น์จ: pฬ = {successes/n:.3f}")
print(f"95% CI: [{lower:.3f}, {upper:.3f}]")
print(f"ํด์: ๋ชจ๋น์จ์ {lower*100:.1f}%์์ {upper*100:.1f}% ์ฌ์ด์ผ ๊ฒ์ผ๋ก ์ถ์ ")
# ์ ๊ท๊ทผ์ฌ ์กฐ๊ฑด ํ์ธ: np โฅ 10 and n(1-p) โฅ 10
p_hat = successes / n
print(f"\n์ ๊ท๊ทผ์ฌ ์กฐ๊ฑด ํ์ธ:")
print(f"np = {n * p_hat:.1f} โฅ 10? {n * p_hat >= 10}")
print(f"n(1-p) = {n * (1-p_hat):.1f} โฅ 10? {n * (1-p_hat) >= 10}")
3.2 Wilson ์ ๋ขฐ๊ตฌ๊ฐ (๊ฐ์ ๋ ๋ฐฉ๋ฒ)¶
def ci_proportion_wilson(successes, n, confidence=0.95):
"""Wilson ์ ๋ขฐ๊ตฌ๊ฐ (๋ ์ ํํ ๋ฐฉ๋ฒ)"""
p_hat = successes / n
z = stats.norm.ppf((1 + confidence) / 2)
denominator = 1 + z**2 / n
center = (p_hat + z**2 / (2*n)) / denominator
margin = (z / denominator) * np.sqrt(p_hat*(1-p_hat)/n + z**2/(4*n**2))
return center - margin, center + margin
# ์์ ํ๋ณธ์์ ๋น๊ต
successes = 8
n = 20
p_hat = successes / n
lower_normal, upper_normal = ci_proportion(successes, n, 0.95)
lower_wilson, upper_wilson = ci_proportion_wilson(successes, n, 0.95)
print(f"ํ๋ณธ๋น์จ: pฬ = {p_hat:.3f} (n={n})")
print(f"\n์ ๊ท๊ทผ์ฌ CI: [{lower_normal:.3f}, {upper_normal:.3f}]")
print(f"Wilson CI: [{lower_wilson:.3f}, {upper_wilson:.3f}]")
# scipy์ proportion_confint
from statsmodels.stats.proportion import proportion_confint
ci_wilson = proportion_confint(successes, n, alpha=0.05, method='wilson')
ci_normal = proportion_confint(successes, n, alpha=0.05, method='normal')
print(f"\nstatsmodels ๊ฒฐ๊ณผ:")
print(f" Normal: [{ci_normal[0]:.3f}, {ci_normal[1]:.3f}]")
print(f" Wilson: [{ci_wilson[0]:.3f}, {ci_wilson[1]:.3f}]")
3.3 ๋น์จ์ ์ ๋ขฐ๊ตฌ๊ฐ ๋น๊ต¶
from statsmodels.stats.proportion import proportion_confint
# ๋ค์ํ ๋ฐฉ๋ฒ ๋น๊ต
successes = 15
n = 50
p_hat = successes / n
methods = ['normal', 'wilson', 'jeffreys', 'agresti_coull', 'beta']
print(f"ํ๋ณธ๋น์จ = {p_hat:.3f} (successes={successes}, n={n})")
print("\n๋น์จ ์ ๋ขฐ๊ตฌ๊ฐ ๋ฐฉ๋ฒ ๋น๊ต:")
print("-" * 50)
for method in methods:
lower, upper = proportion_confint(successes, n, alpha=0.05, method=method)
width = upper - lower
print(f"{method:<15}: [{lower:.4f}, {upper:.4f}], ํญ={width:.4f}")
# ์๊ฐํ
fig, ax = plt.subplots(figsize=(10, 6))
y_positions = range(len(methods))
for i, method in enumerate(methods):
lower, upper = proportion_confint(successes, n, alpha=0.05, method=method)
ax.barh(i, upper-lower, left=lower, height=0.6, alpha=0.7)
ax.scatter([p_hat], [i], color='red', s=50, zorder=5)
ax.axvline(p_hat, color='red', linestyle='--', alpha=0.5, label='ํ๋ณธ๋น์จ')
ax.set_yticks(y_positions)
ax.set_yticklabels(methods)
ax.set_xlabel('๋น์จ')
ax.set_title('๋น์จ ์ ๋ขฐ๊ตฌ๊ฐ ๋ฐฉ๋ฒ ๋น๊ต')
ax.legend()
plt.tight_layout()
plt.show()
4. ๋ชจ๋ถ์ฐ์ ์ ๋ขฐ๊ตฌ๊ฐ¶
4.1 ์นด์ด์ ๊ณฑ ๋ถํฌ๋ฅผ ์ด์ฉํ ์ ๋ขฐ๊ตฌ๊ฐ¶
def ci_variance(sample, confidence=0.95):
"""๋ชจ๋ถ์ฐ์ ์ ๋ขฐ๊ตฌ๊ฐ (์ ๊ท๋ชจ์ง๋จ ๊ฐ์ )"""
n = len(sample)
s_squared = np.var(sample, ddof=1)
alpha = 1 - confidence
chi2_lower = stats.chi2.ppf(alpha/2, df=n-1)
chi2_upper = stats.chi2.ppf(1 - alpha/2, df=n-1)
# ์ ๋ขฐ๊ตฌ๊ฐ: ((n-1)sยฒ / ฯยฒ_upper, (n-1)sยฒ / ฯยฒ_lower)
var_lower = (n - 1) * s_squared / chi2_upper
var_upper = (n - 1) * s_squared / chi2_lower
return var_lower, var_upper
def ci_std(sample, confidence=0.95):
"""๋ชจํ์คํธ์ฐจ์ ์ ๋ขฐ๊ตฌ๊ฐ"""
var_lower, var_upper = ci_variance(sample, confidence)
return np.sqrt(var_lower), np.sqrt(var_upper)
# ์์: ํ์ง๊ด๋ฆฌ์์ ๋ถ์ฐ ์ถ์
np.random.seed(42)
sample = np.random.normal(100, 5, 30) # ์ค์ ฯ = 5
s_squared = np.var(sample, ddof=1)
s = np.std(sample, ddof=1)
var_lower, var_upper = ci_variance(sample, 0.95)
std_lower, std_upper = ci_std(sample, 0.95)
print("๋ชจ๋ถ์ฐ์ 95% ์ ๋ขฐ๊ตฌ๊ฐ:")
print(f"ํ๋ณธ๋ถ์ฐ: sยฒ = {s_squared:.3f}")
print(f"๋ถ์ฐ CI: [{var_lower:.3f}, {var_upper:.3f}]")
print(f"\n๋ชจํ์คํธ์ฐจ์ 95% ์ ๋ขฐ๊ตฌ๊ฐ:")
print(f"ํ๋ณธํ์คํธ์ฐจ: s = {s:.3f}")
print(f"ํ์คํธ์ฐจ CI: [{std_lower:.3f}, {std_upper:.3f}]")
print(f"์ค์ ฯ = 5๊ฐ ๊ตฌ๊ฐ์ ํฌํจ๋๋๊ฐ? {std_lower <= 5 <= std_upper}")
4.2 ์นด์ด์ ๊ณฑ ๋ถํฌ์ ๋น๋์นญ์ฑ¶
# ๋ถ์ฐ์ ์ ๋ขฐ๊ตฌ๊ฐ์ด ํ๊ท ์ค์ฌ์ด ์๋ ์ด์
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# ์นด์ด์ ๊ณฑ ๋ถํฌ
df = 20
x = np.linspace(0, 50, 200)
chi2_pdf = stats.chi2.pdf(x, df)
# ์๊ณ๊ฐ
alpha = 0.05
chi2_lower = stats.chi2.ppf(alpha/2, df)
chi2_upper = stats.chi2.ppf(1 - alpha/2, df)
axes[0].plot(x, chi2_pdf, 'b-', lw=2)
axes[0].fill_between(x, chi2_pdf, where=(x >= chi2_lower) & (x <= chi2_upper),
alpha=0.3, color='blue')
axes[0].axvline(chi2_lower, color='r', linestyle='--', label=f'ฯยฒ_L = {chi2_lower:.2f}')
axes[0].axvline(chi2_upper, color='r', linestyle='--', label=f'ฯยฒ_U = {chi2_upper:.2f}')
axes[0].axvline(df, color='g', linestyle=':', label=f'E[ฯยฒ] = {df}')
axes[0].set_xlabel('ฯยฒ')
axes[0].set_ylabel('๋ฐ๋')
axes[0].set_title(f'์นด์ด์ ๊ณฑ ๋ถํฌ (df={df})')
axes[0].legend()
# ๋ถ์ฐ ์ ๋ขฐ๊ตฌ๊ฐ์ ์๋ฎฌ๋ ์ด์
np.random.seed(42)
true_variance = 25 # ฯยฒ = 25
n = 21 # df = 20
n_simulations = 1000
var_estimates = []
ci_lowers = []
ci_uppers = []
for _ in range(n_simulations):
sample = np.random.normal(0, np.sqrt(true_variance), n)
s2 = np.var(sample, ddof=1)
var_estimates.append(s2)
lower = (n-1) * s2 / chi2_upper
upper = (n-1) * s2 / chi2_lower
ci_lowers.append(lower)
ci_uppers.append(upper)
axes[1].hist(var_estimates, bins=50, density=True, alpha=0.7, label='ํ๋ณธ๋ถ์ฐ ๋ถํฌ')
axes[1].axvline(true_variance, color='r', linestyle='--', lw=2,
label=f'ฯยฒ = {true_variance}')
axes[1].axvline(np.mean(var_estimates), color='g', linestyle=':',
label=f'E[sยฒ] = {np.mean(var_estimates):.2f}')
axes[1].set_xlabel('ํ๋ณธ๋ถ์ฐ')
axes[1].set_ylabel('๋ฐ๋')
axes[1].set_title('ํ๋ณธ๋ถ์ฐ์ ๋ถํฌ')
axes[1].legend()
plt.tight_layout()
plt.show()
# ์ปค๋ฒ๋ฆฌ์ง ํ์ธ
coverage = np.mean([(l <= true_variance <= u)
for l, u in zip(ci_lowers, ci_uppers)])
print(f"95% ์ ๋ขฐ๊ตฌ๊ฐ์ ์ค์ ์ปค๋ฒ๋ฆฌ์ง: {coverage*100:.1f}%")
5. ๋ ์ง๋จ ๋น๊ต๋ฅผ ์ํ ์ ๋ขฐ๊ตฌ๊ฐ¶
5.1 ๋ ๋ชจํ๊ท ์ฐจ์ด์ ์ ๋ขฐ๊ตฌ๊ฐ¶
def ci_two_means_independent(sample1, sample2, confidence=0.95, equal_var=True):
"""๋
๋ฆฝ ๋ ํ๋ณธ ํ๊ท ์ฐจ์ด์ ์ ๋ขฐ๊ตฌ๊ฐ"""
n1, n2 = len(sample1), len(sample2)
x1_bar, x2_bar = sample1.mean(), sample2.mean()
s1, s2 = sample1.std(ddof=1), sample2.std(ddof=1)
if equal_var:
# ํฉ๋๋ถ์ฐ (pooled variance)
sp_squared = ((n1-1)*s1**2 + (n2-1)*s2**2) / (n1 + n2 - 2)
se = np.sqrt(sp_squared * (1/n1 + 1/n2))
df = n1 + n2 - 2
else:
# Welch's t-test (๋ฑ๋ถ์ฐ ๊ฐ์ X)
se = np.sqrt(s1**2/n1 + s2**2/n2)
# Welch-Satterthwaite ์์ ๋
df = (s1**2/n1 + s2**2/n2)**2 / \
((s1**2/n1)**2/(n1-1) + (s2**2/n2)**2/(n2-1))
t_critical = stats.t.ppf((1 + confidence) / 2, df)
margin = t_critical * se
diff = x1_bar - x2_bar
return diff - margin, diff + margin, diff, df
# ์์: A/B ํ
์คํธ ๊ฒฐ๊ณผ
np.random.seed(42)
group_A = np.random.normal(105, 15, 50) # ์ฒ๋ฆฌ๊ตฐ
group_B = np.random.normal(100, 15, 50) # ๋์กฐ๊ตฐ
# ๋ฑ๋ถ์ฐ ๊ฐ์
lower_eq, upper_eq, diff, df = ci_two_means_independent(group_A, group_B,
equal_var=True)
print("๋ ๋ชจํ๊ท ์ฐจ์ด์ 95% ์ ๋ขฐ๊ตฌ๊ฐ:")
print(f"๊ทธ๋ฃน A ํ๊ท : {group_A.mean():.2f}, ๊ทธ๋ฃน B ํ๊ท : {group_B.mean():.2f}")
print(f"์ฐจ์ด (A - B): {diff:.2f}")
print(f"๋ฑ๋ถ์ฐ ๊ฐ์ CI (df={df:.0f}): [{lower_eq:.2f}, {upper_eq:.2f}]")
# ๋ฑ๋ถ์ฐ ๊ฐ์ X (Welch)
lower_welch, upper_welch, diff, df_welch = ci_two_means_independent(group_A, group_B,
equal_var=False)
print(f"Welch CI (df={df_welch:.1f}): [{lower_welch:.2f}, {upper_welch:.2f}]")
# scipy ํ์ธ
from scipy.stats import ttest_ind
t_stat, p_val = ttest_ind(group_A, group_B, equal_var=False)
print(f"\nscipy t-๊ฒ์ ํต๊ณ๋: {t_stat:.3f}, p-value: {p_val:.4f}")
5.2 ๋์ํ๋ณธ ํ๊ท ์ฐจ์ด์ ์ ๋ขฐ๊ตฌ๊ฐ¶
def ci_paired_difference(before, after, confidence=0.95):
"""๋์ํ๋ณธ ํ๊ท ์ฐจ์ด์ ์ ๋ขฐ๊ตฌ๊ฐ"""
diff = after - before
n = len(diff)
d_bar = diff.mean()
s_d = diff.std(ddof=1)
se = s_d / np.sqrt(n)
t_critical = stats.t.ppf((1 + confidence) / 2, df=n-1)
margin = t_critical * se
return d_bar - margin, d_bar + margin, d_bar
# ์์: ๋ค์ด์ดํธ ํ๋ก๊ทธ๋จ ์ ํ ์ฒด์ค
np.random.seed(42)
weight_before = np.random.normal(75, 10, 30)
weight_after = weight_before - np.random.normal(3, 2, 30) # ํ๊ท 3kg ๊ฐ์
lower, upper, mean_diff = ci_paired_difference(weight_before, weight_after, 0.95)
print("๋์ํ๋ณธ ํ๊ท ์ฐจ์ด์ 95% ์ ๋ขฐ๊ตฌ๊ฐ:")
print(f"์ : {weight_before.mean():.2f} kg, ํ: {weight_after.mean():.2f} kg")
print(f"ํ๊ท ๋ณํ: {mean_diff:.2f} kg")
print(f"95% CI: [{lower:.2f}, {upper:.2f}] kg")
if upper < 0:
print("โ ์ ๋ขฐ๊ตฌ๊ฐ์ด 0์ ํฌํจํ์ง ์์ผ๋ฏ๋ก, ์ฒด์ค ๊ฐ์๊ฐ ์ ์ํจ")
5.3 ๋ ๋ชจ๋น์จ ์ฐจ์ด์ ์ ๋ขฐ๊ตฌ๊ฐ¶
def ci_two_proportions(successes1, n1, successes2, n2, confidence=0.95):
"""๋ ๋ชจ๋น์จ ์ฐจ์ด์ ์ ๋ขฐ๊ตฌ๊ฐ"""
p1 = successes1 / n1
p2 = successes2 / n2
diff = p1 - p2
se = np.sqrt(p1*(1-p1)/n1 + p2*(1-p2)/n2)
z_critical = stats.norm.ppf((1 + confidence) / 2)
margin = z_critical * se
return diff - margin, diff + margin, diff
# ์์: ๋ ๊ด๊ณ ์ ํด๋ฆญ๋ฅ ๋น๊ต
# ๊ด๊ณ A: 1000๋ช
๋
ธ์ถ, 150๋ช
ํด๋ฆญ
# ๊ด๊ณ B: 1200๋ช
๋
ธ์ถ, 132๋ช
ํด๋ฆญ
lower, upper, diff = ci_two_proportions(150, 1000, 132, 1200, 0.95)
print("๋ ๋ชจ๋น์จ ์ฐจ์ด์ 95% ์ ๋ขฐ๊ตฌ๊ฐ:")
print(f"๊ด๊ณ A ํด๋ฆญ๋ฅ : {150/1000:.3f}")
print(f"๊ด๊ณ B ํด๋ฆญ๋ฅ : {132/1200:.3f}")
print(f"์ฐจ์ด: {diff:.4f} ({diff*100:.2f}%p)")
print(f"95% CI: [{lower:.4f}, {upper:.4f}]")
if lower > 0:
print("โ ๊ด๊ณ A์ ํด๋ฆญ๋ฅ ์ด ์ ์ํ๊ฒ ๋์")
elif upper < 0:
print("โ ๊ด๊ณ B์ ํด๋ฆญ๋ฅ ์ด ์ ์ํ๊ฒ ๋์")
else:
print("โ ๋ ๊ด๊ณ ์ ํด๋ฆญ๋ฅ ์ ์ ์ํ ์ฐจ์ด ์์")
6. ๋ถํธ์คํธ๋ฉ ์ ๋ขฐ๊ตฌ๊ฐ¶
6.1 ๋ถํธ์คํธ๋ฉ ๋ฐฉ๋ฒ ์๊ฐ¶
def bootstrap_ci(data, statistic_func, confidence=0.95, n_bootstrap=10000):
"""๋ถํธ์คํธ๋ฉ ์ ๋ขฐ๊ตฌ๊ฐ (๋ฐฑ๋ถ์์ ๋ฐฉ๋ฒ)"""
np.random.seed(42)
n = len(data)
bootstrap_statistics = []
for _ in range(n_bootstrap):
# ๋ณต์์ถ์ถ๋ก ๋ถํธ์คํธ๋ฉ ํ๋ณธ ์์ฑ
bootstrap_sample = np.random.choice(data, size=n, replace=True)
bootstrap_statistics.append(statistic_func(bootstrap_sample))
# ๋ฐฑ๋ถ์์ ์ ๋ขฐ๊ตฌ๊ฐ
alpha = 1 - confidence
lower = np.percentile(bootstrap_statistics, 100 * alpha / 2)
upper = np.percentile(bootstrap_statistics, 100 * (1 - alpha / 2))
return lower, upper, np.array(bootstrap_statistics)
# ์์: ํ๊ท ์ ๋ถํธ์คํธ๋ฉ ์ ๋ขฐ๊ตฌ๊ฐ
np.random.seed(42)
sample = np.random.exponential(scale=5, size=50) # ๋น๋์นญ ๋ถํฌ
lower_boot, upper_boot, boot_means = bootstrap_ci(sample, np.mean, 0.95)
# ๊ธฐ์กด t-๋ถํฌ ๋ฐฉ๋ฒ๊ณผ ๋น๊ต
lower_t, upper_t, _ = ci_mean_t(sample, 0.95)
print("ํ๊ท ์ 95% ์ ๋ขฐ๊ตฌ๊ฐ ๋น๊ต:")
print(f"ํ๋ณธํ๊ท : {sample.mean():.3f}")
print(f"t-๋ถํฌ: [{lower_t:.3f}, {upper_t:.3f}]")
print(f"๋ถํธ์คํธ๋ฉ: [{lower_boot:.3f}, {upper_boot:.3f}]")
# ์๊ฐํ
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# ์๋ณธ ๋ฐ์ดํฐ
axes[0].hist(sample, bins=20, density=True, alpha=0.7, edgecolor='black')
axes[0].axvline(sample.mean(), color='r', linestyle='--', linewidth=2, label='ํ๋ณธํ๊ท ')
axes[0].set_xlabel('๊ฐ')
axes[0].set_ylabel('๋ฐ๋')
axes[0].set_title('์๋ณธ ๋ฐ์ดํฐ (์ง์๋ถํฌ)')
axes[0].legend()
# ๋ถํธ์คํธ๋ฉ ๋ถํฌ
axes[1].hist(boot_means, bins=50, density=True, alpha=0.7, edgecolor='black')
axes[1].axvline(lower_boot, color='r', linestyle='--', label=f'95% CI: [{lower_boot:.2f}, {upper_boot:.2f}]')
axes[1].axvline(upper_boot, color='r', linestyle='--')
axes[1].axvline(sample.mean(), color='g', linestyle='-', linewidth=2, label='ํ๋ณธํ๊ท ')
axes[1].set_xlabel('๋ถํธ์คํธ๋ฉ ํ๋ณธํ๊ท ')
axes[1].set_ylabel('๋ฐ๋')
axes[1].set_title('๋ถํธ์คํธ๋ฉ ๋ถํฌ (10,000ํ)')
axes[1].legend()
plt.tight_layout()
plt.show()
6.2 ๋ค์ํ ๋ถํธ์คํธ๋ฉ ๋ฐฉ๋ฒ¶
def bootstrap_ci_bca(data, statistic_func, confidence=0.95, n_bootstrap=10000):
"""BCa (Bias-Corrected and Accelerated) ๋ถํธ์คํธ๋ฉ ์ ๋ขฐ๊ตฌ๊ฐ"""
from scipy.stats import norm
np.random.seed(42)
n = len(data)
original_stat = statistic_func(data)
# ๋ถํธ์คํธ๋ฉ ํต๊ณ๋
boot_stats = []
for _ in range(n_bootstrap):
boot_sample = np.random.choice(data, size=n, replace=True)
boot_stats.append(statistic_func(boot_sample))
boot_stats = np.array(boot_stats)
# ํธํฅ ์์ (bias correction)
z0 = norm.ppf(np.mean(boot_stats < original_stat))
# ๊ฐ์ ๊ณ์ (acceleration) - jackknife ์ฌ์ฉ
jackknife_stats = []
for i in range(n):
jack_sample = np.delete(data, i)
jackknife_stats.append(statistic_func(jack_sample))
jackknife_stats = np.array(jackknife_stats)
jack_mean = jackknife_stats.mean()
a = np.sum((jack_mean - jackknife_stats)**3) / \
(6 * (np.sum((jack_mean - jackknife_stats)**2))**1.5 + 1e-10)
# BCa ๋ฐฑ๋ถ์์ ๊ณ์ฐ
alpha = 1 - confidence
z_alpha_lower = norm.ppf(alpha / 2)
z_alpha_upper = norm.ppf(1 - alpha / 2)
alpha_lower = norm.cdf(z0 + (z0 + z_alpha_lower) / (1 - a * (z0 + z_alpha_lower)))
alpha_upper = norm.cdf(z0 + (z0 + z_alpha_upper) / (1 - a * (z0 + z_alpha_upper)))
lower = np.percentile(boot_stats, 100 * alpha_lower)
upper = np.percentile(boot_stats, 100 * alpha_upper)
return lower, upper
# ๋น๊ต
np.random.seed(42)
sample = np.random.exponential(scale=5, size=30)
# ๋ฐฑ๋ถ์์ ๋ฐฉ๋ฒ
lower_pct, upper_pct, _ = bootstrap_ci(sample, np.mean, 0.95)
# BCa ๋ฐฉ๋ฒ
lower_bca, upper_bca = bootstrap_ci_bca(sample, np.mean, 0.95)
print("๋ถํธ์คํธ๋ฉ ๋ฐฉ๋ฒ ๋น๊ต:")
print(f"ํ๋ณธํ๊ท : {sample.mean():.3f}")
print(f"๋ฐฑ๋ถ์์: [{lower_pct:.3f}, {upper_pct:.3f}]")
print(f"BCa: [{lower_bca:.3f}, {upper_bca:.3f}]")
6.3 ์ค์๊ฐ์ ๋ถํธ์คํธ๋ฉ ์ ๋ขฐ๊ตฌ๊ฐ¶
# ์ค์๊ฐ์ฒ๋ผ ๋ถํฌ๋ฅผ ๋ชจ๋ฅด๋ ํต๊ณ๋์ ๋ถํธ์คํธ๋ฉ ํ์ฉ
np.random.seed(42)
sample = np.random.lognormal(mean=3, sigma=0.5, size=100)
lower_median, upper_median, boot_medians = bootstrap_ci(sample, np.median, 0.95)
print("์ค์๊ฐ์ 95% ๋ถํธ์คํธ๋ฉ ์ ๋ขฐ๊ตฌ๊ฐ:")
print(f"ํ๋ณธ์ค์๊ฐ: {np.median(sample):.3f}")
print(f"95% CI: [{lower_median:.3f}, {upper_median:.3f}]")
# ์๊ด๊ณ์์ ๋ถํธ์คํธ๋ฉ ์ ๋ขฐ๊ตฌ๊ฐ
np.random.seed(42)
x = np.random.normal(0, 1, 50)
y = 0.7 * x + np.random.normal(0, 0.5, 50)
data_combined = np.column_stack([x, y])
def correlation(data):
return np.corrcoef(data[:, 0], data[:, 1])[0, 1]
lower_corr, upper_corr, boot_corrs = bootstrap_ci(data_combined, correlation, 0.95)
print(f"\n์๊ด๊ณ์์ 95% ๋ถํธ์คํธ๋ฉ ์ ๋ขฐ๊ตฌ๊ฐ:")
print(f"ํ๋ณธ์๊ด๊ณ์: {np.corrcoef(x, y)[0, 1]:.3f}")
print(f"95% CI: [{lower_corr:.3f}, {upper_corr:.3f}]")
7. scipy๋ฅผ ์ด์ฉํ ์ ๋ขฐ๊ตฌ๊ฐ ๊ณ์ฐ¶
7.1 ๊ธฐ๋ณธ ์ ๋ขฐ๊ตฌ๊ฐ ํจ์¶
# scipy.stats์ interval ๋ฉ์๋ ํ์ฉ
# ์ ๊ท๋ถํฌ์์ ๋ชจํ๊ท ์ ๋ขฐ๊ตฌ๊ฐ
np.random.seed(42)
sample = np.random.normal(100, 15, 50)
# t-๋ถํฌ ์ด์ฉ
mean = sample.mean()
sem = stats.sem(sample) # ํ์ค์ค์ฐจ
ci_95 = stats.t.interval(confidence=0.95, df=len(sample)-1, loc=mean, scale=sem)
ci_99 = stats.t.interval(confidence=0.99, df=len(sample)-1, loc=mean, scale=sem)
print("scipy.stats.t.interval ์ฌ์ฉ:")
print(f"ํ๋ณธํ๊ท : {mean:.2f}")
print(f"95% CI: [{ci_95[0]:.2f}, {ci_95[1]:.2f}]")
print(f"99% CI: [{ci_99[0]:.2f}, {ci_99[1]:.2f}]")
# bootstrap ๋ฉ์๋ (scipy >= 1.9)
from scipy.stats import bootstrap
np.random.seed(42)
sample_tuple = (sample,) # tuple๋ก ์ ๋ฌํด์ผ ํจ
result = bootstrap(sample_tuple, np.mean, confidence_level=0.95, n_resamples=9999)
print(f"\nscipy.stats.bootstrap ์ฌ์ฉ:")
print(f"95% CI: [{result.confidence_interval.low:.2f}, {result.confidence_interval.high:.2f}]")
7.2 statsmodels ํ์ฉ¶
import statsmodels.api as sm
from statsmodels.stats.weightstats import DescrStatsW
# DescrStatsW๋ก ์ ๋ขฐ๊ตฌ๊ฐ ๊ณ์ฐ
np.random.seed(42)
sample = np.random.normal(100, 15, 50)
d = DescrStatsW(sample)
print("statsmodels DescrStatsW ์ฌ์ฉ:")
print(f"ํ๋ณธํ๊ท : {d.mean:.2f}")
print(f"95% CI: {d.tconfint_mean(alpha=0.05)}")
print(f"99% CI: {d.tconfint_mean(alpha=0.01)}")
# ๋ ํ๋ณธ ๋น๊ต
group1 = np.random.normal(100, 15, 40)
group2 = np.random.normal(105, 15, 45)
from statsmodels.stats.weightstats import CompareMeans
cm = CompareMeans(DescrStatsW(group1), DescrStatsW(group2))
print(f"\n๋ ํ๊ท ์ฐจ์ด์ 95% CI: {cm.tconfint_diff(alpha=0.05)}")
์ฐ์ต ๋ฌธ์ ¶
๋ฌธ์ 1: t-์ ๋ขฐ๊ตฌ๊ฐ¶
๋ค์ ํ๋ณธ์์ ๋ชจํ๊ท ์ 95% ์ ๋ขฐ๊ตฌ๊ฐ์ ๊ตฌํ์์ค.
sample = [23, 25, 28, 22, 26, 24, 27, 25, 29, 24]
๋ฌธ์ 2: ๋น์จ์ ์ ๋ขฐ๊ตฌ๊ฐ¶
500๋ช ์ ๋์์ผ๋ก ์ค๋ฌธ์กฐ์ฌํ ๊ฒฐ๊ณผ 230๋ช ์ด ์ฐฌ์ฑํ์ต๋๋ค. - (a) ๋ชจ๋น์จ์ 95% ์ ๋ขฐ๊ตฌ๊ฐ์ ๊ตฌํ์์ค (์ ๊ท๊ทผ์ฌ) - (b) Wilson ๋ฐฉ๋ฒ์ผ๋ก ๋ค์ ๊ณ์ฐํ์์ค - (c) ํ๋ณธ ํฌ๊ธฐ๋ฅผ 2000๋ช ์ผ๋ก ๋๋ฆฌ๋ฉด ๊ตฌ๊ฐ ํญ์ด ์ด๋ป๊ฒ ๋ณํ๋๊ฐ?
๋ฌธ์ 3: ๋ถํธ์คํธ๋ฉ¶
๋ค์ ๋น๋์นญ ๋ถํฌ์์ ํ๊ท ๊ณผ ์ค์๊ฐ์ ๋ถํธ์คํธ๋ฉ 95% ์ ๋ขฐ๊ตฌ๊ฐ์ ๊ฐ๊ฐ ๊ตฌํ์์ค.
np.random.seed(42)
data = np.random.exponential(10, 40)
์ ๋ฆฌ¶
| ์ ๋ขฐ๊ตฌ๊ฐ ์ ํ | ์กฐ๊ฑด | ๊ณต์ | Python |
|---|---|---|---|
| ํ๊ท (ฯ ๊ธฐ์ง) | Z-๊ตฌ๊ฐ | xฬ ยฑ zยทฯ/โn | stats.norm.interval() |
| ํ๊ท (ฯ ๋ฏธ์ง) | t-๊ตฌ๊ฐ | xฬ ยฑ tยทs/โn | stats.t.interval() |
| ๋น์จ | np โฅ 10 | pฬ ยฑ zยทโ(pฬ(1-pฬ)/n) | proportion_confint() |
| ๋ถ์ฐ | ์ ๊ท๋ชจ์ง๋จ | ฯยฒ ๋ถํฌ ์ด์ฉ | ์ง์ ๊ณ์ฐ |
| ๋ถํธ์คํธ๋ฉ | ๋น๋ชจ์์ | ์ฌํ๋ณธ์ถ์ถ | stats.bootstrap() |