import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
# Общая σ для всех сюжетов
SIGMA = 0.35
def _fill_tail(ax, x, y, mask, **kwargs):
if mask.any():
ax.fill_between(x[mask], y[mask], **kwargs)
def plot_hypothesis_test(mu0, sigma, n, x_bar, alpha=0.05, kind='left', title=''):
from matplotlib.patches import ConnectionPatch
se = sigma / np.sqrt(n)
z_score = (x_bar - mu0) / se
z_abs = abs(z_score)
fig, (ax_top, ax_bottom) = plt.subplots(2, 1, figsize=(8, 9), height_ratios=[1, 1])
fig.subplots_adjust(hspace=0.38)
x_grid = np.linspace(mu0 - 4 * se, mu0 + 4 * se, 1000)
y_top = norm.pdf(x_grid, loc=mu0, scale=se)
z_grid = np.linspace(-4, 4, 1000)
y_bot = norm.pdf(z_grid)
ax_top.plot(x_grid, y_top, lw=2, color='0.25', label=r'$\bar{X} \mid H_0$')
ax_bottom.plot(z_grid, y_bot, lw=2, color='0.25', label=r'$Z$')
def link(x_val, z_val, color):
con = ConnectionPatch(
xyA=(x_val, 0),
coordsA=ax_top.get_xaxis_transform(),
xyB=(z_val, 0),
coordsB=ax_bottom.get_xaxis_transform(),
color=color,
ls='--',
lw=1.8,
axesA=ax_top,
axesB=ax_bottom,
)
ax_bottom.add_artist(con)
p_label = None
if kind == 'left':
z_crit = norm.ppf(alpha)
K = mu0 + z_crit * se
p_value = norm.cdf(z_score)
reject = x_bar < K
print(f'K = {K:.3f}')
print('Решение (граница K):', 'Отклоняем H0' if reject else 'Не отклоняем H0')
ax_top.axvline(K, color='tomato', ls='--', lw=2, label=rf'$K={K:.3f}$')
ax_bottom.axvline(z_crit, color='tomato', ls='--', lw=2, label=rf'$z_\alpha={z_crit:.3f}$')
link(K, z_crit, 'tomato')
_fill_tail(ax_top, x_grid, y_top, x_grid <= K, color='tomato', alpha=0.35, label=r'Критическая зона ($\alpha$)')
_fill_tail(ax_bottom, z_grid, y_bot, z_grid <= z_crit, color='tomato', alpha=0.35)
p_label = rf'p-value = {p_value:.4f}'
_fill_tail(ax_top, x_grid, y_top, x_grid <= x_bar, color='steelblue', alpha=0.4, label=p_label)
_fill_tail(ax_bottom, z_grid, y_bot, z_grid <= z_score, color='steelblue', alpha=0.4)
elif kind == 'right':
z_crit = norm.ppf(1 - alpha)
K = mu0 + z_crit * se
p_value = 1 - norm.cdf(z_score)
reject = x_bar > K
print(f'K = {K:.3f}')
print('Решение (граница K):', 'Отклоняем H0' if reject else 'Не отклоняем H0')
ax_top.axvline(K, color='tomato', ls='--', lw=2, label=rf'$K={K:.3f}$')
ax_bottom.axvline(z_crit, color='tomato', ls='--', lw=2, label=rf'$z_\alpha={z_crit:.3f}$')
link(K, z_crit, 'tomato')
_fill_tail(ax_top, x_grid, y_top, x_grid >= K, color='tomato', alpha=0.35, label=r'Критическая зона ($\alpha$)')
_fill_tail(ax_bottom, z_grid, y_bot, z_grid >= z_crit, color='tomato', alpha=0.35)
p_label = rf'p-value = {p_value:.4f}'
_fill_tail(ax_top, x_grid, y_top, x_grid >= x_bar, color='steelblue', alpha=0.4, label=p_label)
_fill_tail(ax_bottom, z_grid, y_bot, z_grid >= z_score, color='steelblue', alpha=0.4)
elif kind == 'two-sided':
z_half = norm.ppf(1 - alpha / 2)
K_L = mu0 - z_half * se
K_R = mu0 + z_half * se
z_crit_l, z_crit_r = -z_half, z_half
p_value = 2 * (1 - norm.cdf(z_abs))
reject = abs(z_score) > z_half
x_cut_l = mu0 - z_abs * se
x_cut_r = mu0 + z_abs * se
print(f'K_L = {K_L:.3f}, K_R = {K_R:.3f}')
print('Решение (границы):', 'Отклоняем H0' if reject else 'Не отклоняем H0')
ax_top.axvline(K_L, color='tomato', ls='--', lw=2, label=rf'$K_L={K_L:.3f}$')
ax_top.axvline(K_R, color='tomato', ls='--', lw=2, label=rf'$K_R={K_R:.3f}$')
ax_bottom.axvline(z_crit_l, color='tomato', ls='--', lw=2, label=rf'$-z_{{\alpha/2}}={z_crit_l:.3f}$')
ax_bottom.axvline(z_crit_r, color='tomato', ls='--', lw=2, label=rf'$+z_{{\alpha/2}}={z_crit_r:.3f}$')
link(K_L, z_crit_l, 'tomato')
link(K_R, z_crit_r, 'tomato')
# границы p-value (симметричные относительно mu0)
ax_top.axvline(x_cut_l, color='steelblue', ls=':', lw=1.2)
ax_top.axvline(x_cut_r, color='steelblue', ls=':', lw=1.2)
ax_bottom.axvline(-z_abs, color='steelblue', ls=':', lw=1.2)
ax_bottom.axvline(z_abs, color='steelblue', ls=':', lw=1.2)
_fill_tail(ax_top, x_grid, y_top, x_grid <= K_L, color='tomato', alpha=0.35, label=r'Критическая зона ($\alpha$)')
_fill_tail(ax_top, x_grid, y_top, x_grid >= K_R, color='tomato', alpha=0.35)
_fill_tail(ax_bottom, z_grid, y_bot, z_grid <= z_crit_l, color='tomato', alpha=0.35)
_fill_tail(ax_bottom, z_grid, y_bot, z_grid >= z_crit_r, color='tomato', alpha=0.35)
p_label = rf'p-value = {p_value:.4f} (два хвоста)'
# два отдельных хвоста — иначе fill_between «склеивает» середину
_fill_tail(ax_top, x_grid, y_top, x_grid <= x_cut_l, color='steelblue', alpha=0.4, label=p_label)
_fill_tail(ax_top, x_grid, y_top, x_grid >= x_cut_r, color='steelblue', alpha=0.4)
_fill_tail(ax_bottom, z_grid, y_bot, z_grid <= -z_abs, color='steelblue', alpha=0.4)
_fill_tail(ax_bottom, z_grid, y_bot, z_grid >= z_abs, color='steelblue', alpha=0.4)
else:
raise ValueError("kind должен быть 'left', 'right' или 'two-sided'")
ax_top.axvline(mu0, color='gray', ls=':', lw=1)
ax_bottom.axvline(0, color='gray', ls=':', lw=1)
ax_top.axvline(x_bar, color='navy', lw=2.5, label=rf'$\bar{{x}}={x_bar:.3f}$')
ax_bottom.axvline(z_score, color='navy', lw=2.5, label=rf'$z_{{score}}={z_score:.3f}$')
link(x_bar, z_score, 'navy')
ax_top.set_title('Исходная шкала')
ax_bottom.set_title('Стандартная нормальная шкала')
ax_top.set_xlabel(r'$\bar{X}$')
ax_bottom.set_xlabel(r'$Z$')
ax_top.set_ylabel('Плотность')
ax_bottom.set_ylabel('Плотность')
ax_top.legend(loc='best', fontsize=9)
ax_bottom.legend(loc='best', fontsize=9)
ax_top.grid(alpha=0.2)
ax_bottom.grid(alpha=0.2)
if title:
fig.suptitle(title, y=1.01, fontsize=12)
print(f'$z_{{score}}$ = {z_score:.4f}')
print(f'$p$-value = {p_value:.6f}')
print('Решение (score / p-value):', 'Отклоняем H0' if p_value < alpha else 'Не отклоняем H0')
plt.show()Проверка гипотезы о неизвестном матожидании (известная дисперсия)
Разбираем сюжет с жирафами в трех вариантах (длина шеи взрослых особей — порядка \(1.8\)–\(2.4\) м): - левосторонний тест; - правосторонний тест; - двусторонний тест.
Наблюдаемые \(\bar x\) правдоподобны, а консервативные \(\mu_0\) слегка гротескны, но не настолько как рассматривали в лекции, чтобы получились какие-то осязаемые числа. Вы можете подставить гротескные числа консервативных гипотез из лекций и посмотреть, что будет.
Общие условия применимости \(Z\)-теста для среднего: - наблюдения независимы; - либо распределение исследуемой величины нормальное, либо выборка достаточно большая; - известна истинная дисперсия: \(\sigma = 0.35\) м (одна и та же для всех трёх сюжетов); - уровень значимости фиксирован: \(\alpha = 0.05\).
1. Левосторонний тест
Условия задачи: - \(X\) — длина шеи взрослого жирафа (м); в природе типичный диапазон примерно \(1.8\)–\(2.4\) м; - независимая выборка объема \(n = 20\); - наблюдаемое среднее \(\bar x = 2.1\); - консервативная нулевая гипотеза: \(\mu_0 = 2.38\) — исследователи уверены, что в среднем у жирафов большие шеи; - проверяем при \(\alpha = 0.05\): предоставляют ли полученные данные достаточно доказательств в сторону альтернативной гипотезы (\(H_1: \mu < \mu_0\)).
Теория: - гипотезы: \(H_0: \mu = \mu_0\) против \(H_1: \mu < \mu_0\); - при \(H_0\): \[ \bar X \sim \mathcal N\!\left(\mu_0, \frac{\sigma^2}{n}\right); \] - решающая граница задается условием: \[ P_{H_0}(\bar X < K) = \alpha; \] - стандартизуем и делаем замену переменной: \[ P\!\left(\frac{\bar X-\mu_0}{\sigma/\sqrt n} < \frac{K-\mu_0}{\sigma/\sqrt n}\right)=\alpha \Rightarrow P(Z<z_\alpha)=\alpha; \]
- нужно понимать, что так как \(\alpha\) по своей логике должна быть небольшой вероятностью и по здравому смыслу меньше \(0.5\), то в уравнении выше эта точка по факту будет отрицательной. Можно обозначить ее \((-|z_{\alpha}|)\), чтобы не было сомнений.
- восстанавливаем границу: \[ \frac{K-\mu_0}{\sigma/\sqrt n}=-|z_{\alpha}| \Rightarrow K=\mu_0 - |z_\alpha| \frac{\sigma}{\sqrt n}; \]
- правило: отклоняем \(H_0\), если \(\bar x < K\).
\(z\)-score и \(p\)-value (левосторонний тест)
\(z\)-score — наблюдаемое значение в стандартизованной шкале: \[ z_{\text{score}} = \frac{\bar{x}-\mu_0}{\sigma/\sqrt{n}}. \] Граница \(K\) переходит в критическую точку \(z_\alpha\) (квантиль уровня \(\alpha\)): \[ \frac{K-\mu_0}{\sigma/\sqrt{n}} = z_\alpha. \] Такое преобразования сохраняет отношение порядка: \(\bar{x} < K \Leftrightarrow z_{\text{score}} < z_\alpha\).
Правило через score: отклоняем \(H_0\), если \(z_{\text{score}} < z_\alpha\) (для \(\alpha<0.5\), \(z_\alpha\) - это отрицательное число).
\(p\)-value — вероятность при \(H_0\) получить статистику не менее экстремальную, чем наблюдаемая (для левого теста — не больше \(\bar{x}\)): \[ p\text{-value} = P_{H_0}(\bar{X} \le \bar{x}) = P_{H_0}(Z \le z_{\text{score}}). \]
Правило через \(p\)-value: отклоняем \(H_0\), если \(p\text{-value} < \alpha\).
# Левосторонний тест
mu0 = 2.38
sigma = SIGMA
n = 20
x_bar = 2.1
alpha = 0.05
plot_hypothesis_test(
mu0=mu0,
sigma=sigma,
n=n,
x_bar=x_bar,
alpha=alpha,
kind='left',
title='Левосторонний тест',
)2. Правосторонний тест
Условия задачи: - \(X\) — длина шеи взрослого жирафа (м); - независимая выборка объема \(n = 20\); - наблюдаемое среднее \(\bar x = 2.1\); - консервативная нулевая гипотеза: \(\mu_0 = 1.88\) — исследователи уверены, что в среднем у жирафов маленькие шеи; - проверяем при \(\alpha = 0.05\): предоставляют ли полученные данные достаточно доказательств в сторону альтернативной гипотезы (\(H_1: \mu > \mu_0\)).
Теория: - гипотезы: \(H_0: \mu = \mu_0\) против \(H_1: \mu > \mu_0\); - при \(H_0\): \[ \bar X \sim \mathcal N\!\left(\mu_0, \frac{\sigma^2}{n}\right); \] - решающая граница задается условием: \[ P_{H_0}(\bar X > K) = \alpha; \] - стандартизуем и делаем замену переменной: \[ P\!\left(\frac{\bar X-\mu_0}{\sigma/\sqrt n} > \frac{K-\mu_0}{\sigma/\sqrt n}\right)=\alpha \Rightarrow P(Z>z_{\alpha})=\alpha; \] - снова так как \(\alpha\) по своей логике должна быть небольшой вероятностью и по здравому смыслу меньше \(0.5\), то в уравнении выше эта точка должна быть положительной. Тут не возникнет путаницы, можно продолжать без знака модуля.
- восстанавливаем границу: \[ \frac{K-\mu_0}{\sigma/\sqrt n}=z_{\alpha} \Rightarrow K=\mu_0+z_{\alpha}\frac{\sigma}{\sqrt n}; \]
- правило: отклоняем \(H_0\), если \(\bar x > K\).
\(z\)-score и \(p\)-value (правосторонний тест)
\(z\)-score: \[ z_{\text{score}} = \frac{\bar{x}-\mu_0}{\sigma/\sqrt{n}}. \] Решающая граница \(K\) соответствует критической точке \(z_\alpha\): \[ \frac{K-\mu_0}{\sigma/\sqrt{n}} = z_\alpha. \] Такое преобразования сохраняет отношение порядка: \(\bar{x} > K \Leftrightarrow z_{\text{score}} > z_\alpha\).
Правило через score: отклоняем \(H_0\), если \(z_{\text{score}} > z_\alpha\).
\(p\)-value для правостороннего теста — вероятность при \(H_0\) получить значение не менее экстремальное вправо: \[ p\text{-value} = P_{H_0}(\bar{X} \ge \bar{x}) = P_{H_0}(Z \ge z_{\text{score}}). \]
Правило через \(p\)-value: отклоняем \(H_0\), если \(p\text{-value} < \alpha\).
# Правосторонний тест
mu0 = 1.88
sigma = SIGMA
n = 20
x_bar = 2.1
alpha = 0.05
plot_hypothesis_test(
mu0=mu0,
sigma=sigma,
n=n,
x_bar=x_bar,
alpha=alpha,
kind='right',
title='Правосторонний тест',
)3. Двусторонний тест
Условия задачи: - \(X\) — длина шеи взрослого жирафа (м); - независимая выборка объема \(n = 20\); - наблюдаемое среднее \(\bar x = 2.1\); - консервативная нулевая гипотеза: \(\mu_0 = 2.3\);
Теория: - гипотезы: \(H_0: \mu = \mu_0\) против \(H_1: \mu \neq \mu_0\); - при \(H_0\): \[ \bar X \sim \mathcal N\!\left(\mu_0, \frac{\sigma^2}{n}\right); \] - границы задаются условиями: \[ P_{H_0}(\bar X < K_L)=\frac{\alpha}{2},\qquad P_{H_0}(\bar X > K_R)=\frac{\alpha}{2}; \] - стандартизуем и делаем замену переменной: \[ P\!\left(\frac{K_L-\mu_0}{\sigma/\sqrt n} < Z < \frac{K_R-\mu_0}{\sigma/\sqrt n}\right)=1-\alpha \Rightarrow P(-z_{\alpha/2}<Z<z_{\alpha/2})=1-\alpha; \] - восстанавливаем границы: \[ K_L=\mu_0-z_{\alpha/2}\frac{\sigma}{\sqrt n},\qquad K_R=\mu_0+z_{\alpha/2}\frac{\sigma}{\sqrt n}; \] - правило: отклоняем \(H_0\), если \(\bar x < K_L\) или \(\bar x > K_R\).
\(z\)-score и \(p\)-value (двусторонний тест)
\(z\)-score считается так же: \[ z_{\text{score}} = \frac{\bar{x}-\mu_0}{\sigma/\sqrt{n}}. \] Границы \(K_L, K_R\) переходят в \(\pm z_{\alpha/2}\): \[ K_L = \mu_0 - z_{\alpha/2}\frac{\sigma}{\sqrt{n}}, \qquad K_R = \mu_0 + z_{\alpha/2}\frac{\sigma}{\sqrt{n}}. \]
Правило через score: отклоняем \(H_0\), если \(|z_{\text{score}}| > z_{\alpha/2}\).
\(p\)-value для двустороннего теста — суммарная вероятность обоих хвостов, столь же или более удалённых от \(\mu_0\), чем наблюдение: \[ p\text{-value} = 2\,P_{H_0}(Z \ge |z_{\text{score}}|). \]
Правило через \(p\)-value: отклоняем \(H_0\), если \(p\text{-value} < \alpha\).
# Двусторонний тест
mu0 = 2.3
sigma = SIGMA
n = 20
x_bar = 2.1
alpha = 0.05
plot_hypothesis_test(
mu0=mu0,
sigma=sigma,
n=n,
x_bar=x_bar,
alpha=alpha,
kind='two-sided',
title='Двусторонний тест',
)