z検定の理論と実装

z検定は、母分散が既知であるときに母平均に関する仮説を検証する最も基本的な検定手法です。検定統計量が標準正規分布 $N(0, 1)$ に従うことから「z検定」と呼ばれます。

実務で母分散が既知であることは稀ですが、z検定は検定統計量の構成、棄却域の設定、p値の計算といった仮説検定の基本手続きを最もシンプルな形で学べるため、検定理論の出発点として最適です。

本記事の内容

  • z検定の前提条件
  • 検定統計量 $Z = (\bar{X} – \mu_0)/(\sigma/\sqrt{n})$ の導出
  • 片側検定と両側検定の棄却域
  • p値の計算方法
  • Python での実装と可視化

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

z検定の前提条件

z検定を適用するには、以下のいずれかの条件が必要です。

条件1: 母集団が正規分布に従い、母分散 $\sigma^2$ が既知

この場合、標本サイズ $n$ に関係なくz検定を適用できます。

条件2: 標本サイズが十分大きい($n \geq 30$ が目安)

中心極限定理により、母集団分布の形状によらず標本平均は近似的に正規分布に従います。また大標本では標本分散で母分散を近似できます。

1標本z検定の検定統計量の導出

問題設定

母集団 $X \sim N(\mu, \sigma^2)$($\sigma^2$ 既知)から、大きさ $n$ の無作為標本 $X_1, X_2, \dots, X_n$ を抽出します。仮説は以下のように設定します。

$$ H_0: \mu = \mu_0, \quad H_1: \mu \neq \mu_0 \quad (\text{両側検定}) $$

標本平均の分布

各 $X_i$ は独立に $N(\mu, \sigma^2)$ に従うため、標本平均 $\bar{X} = \frac{1}{n}\sum_{i=1}^{n} X_i$ について、

$$ E[\bar{X}] = \frac{1}{n}\sum_{i=1}^{n} E[X_i] = \mu $$

$$ \text{Var}(\bar{X}) = \frac{1}{n^2}\sum_{i=1}^{n} \text{Var}(X_i) = \frac{\sigma^2}{n} $$

独立な正規確率変数の線形結合は正規分布に従うため、

$$ \bar{X} \sim N\left(\mu, \frac{\sigma^2}{n}\right) $$

標準化

$\bar{X}$ を標準化します。$H_0: \mu = \mu_0$ のもとで、

$$ Z = \frac{\bar{X} – \mu_0}{\sigma / \sqrt{n}} \sim N(0, 1) $$

分母の $\sigma / \sqrt{n}$ は 標準誤差(standard error, SE) と呼ばれ、標本平均のばらつきの大きさを表します。

この $Z$ がz検定の 検定統計量 です。直感的には、「標本平均が帰無仮説の値 $\mu_0$ から標準誤差の何倍離れているか」を表しています。

棄却域の決定

両側検定

対立仮説 $H_1: \mu \neq \mu_0$ では、$\bar{X}$ が $\mu_0$ から上下いずれの方向にも大きく離れれば $H_0$ を棄却します。

有意水準 $\alpha$ のもとで、棄却域は

$$ |Z| > z_{\alpha/2} $$

ここで $z_{\alpha/2}$ は標準正規分布の上側 $\alpha/2$ 点です。$\alpha$ を両端に均等に配分するため $\alpha/2$ で分割します。

$$ P(|Z| > z_{\alpha/2} \mid H_0) = 2P(Z > z_{\alpha/2}) = 2 \cdot \frac{\alpha}{2} = \alpha $$

$\alpha = 0.05$ のとき $z_{0.025} = 1.960$ です。

片側検定(右側)

対立仮説 $H_1: \mu > \mu_0$ の場合、

$$ Z > z_\alpha $$

$\alpha = 0.05$ のとき $z_{0.05} = 1.645$ です。

片側検定(左側)

対立仮説 $H_1: \mu < \mu_0$ の場合、

$$ Z < -z_\alpha $$

p値の計算

両側検定

観測された検定統計量を $z_{\text{obs}}$ とすると、

$$ \begin{align} p &= P(|Z| \geq |z_{\text{obs}}| \mid H_0) \\ &= 2P(Z \geq |z_{\text{obs}}|) \\ &= 2(1 – \Phi(|z_{\text{obs}}|)) \end{align} $$

片側検定(右側)

$$ p = P(Z \geq z_{\text{obs}} \mid H_0) = 1 – \Phi(z_{\text{obs}}) $$

片側検定(左側)

$$ p = P(Z \leq z_{\text{obs}} \mid H_0) = \Phi(z_{\text{obs}}) $$

2標本z検定

問題設定

2つの独立な母集団から標本を抽出し、母平均の差を検定します。

$$ X_1, \dots, X_{n_1} \sim N(\mu_1, \sigma_1^2), \quad Y_1, \dots, Y_{n_2} \sim N(\mu_2, \sigma_2^2) $$

$\sigma_1^2, \sigma_2^2$ は既知とします。

$$ H_0: \mu_1 = \mu_2, \quad H_1: \mu_1 \neq \mu_2 $$

検定統計量の導出

$\bar{X}$ と $\bar{Y}$ は独立であるため、

$$ \bar{X} – \bar{Y} \sim N\left(\mu_1 – \mu_2, \frac{\sigma_1^2}{n_1} + \frac{\sigma_2^2}{n_2}\right) $$

$H_0: \mu_1 – \mu_2 = 0$ のもとで標準化すると、

$$ Z = \frac{\bar{X} – \bar{Y}}{\sqrt{\dfrac{\sigma_1^2}{n_1} + \dfrac{\sigma_2^2}{n_2}}} \sim N(0, 1) $$

具体例

数値例: 1標本z検定

ある工場で製品の長さは $\mu_0 = 50$ mm、$\sigma = 2$ mm と分かっています。新しい機械を導入後、$n = 25$ 個を抽出したところ $\bar{x} = 50.8$ mm でした。長さが変化したかを $\alpha = 0.05$ で検定します。

仮説: $H_0: \mu = 50$, $H_1: \mu \neq 50$

検定統計量:

$$ Z = \frac{50.8 – 50}{2 / \sqrt{25}} = \frac{0.8}{0.4} = 2.0 $$

p値:

$$ p = 2(1 – \Phi(2.0)) = 2(1 – 0.9772) = 0.0456 $$

判定: $p = 0.0456 < 0.05$ なので $H_0$ を棄却します。製品の長さに有意な変化があると結論します。

Pythonでの実装

1標本z検定のスクラッチ実装

import numpy as np
from scipy import stats

def z_test_1sample(data, mu_0, sigma, alternative='two-sided'):
    """
    1標本z検定

    Parameters
    ----------
    data : array-like
        標本データ
    mu_0 : float
        帰無仮説の母平均
    sigma : float
        母標準偏差(既知)
    alternative : str
        'two-sided', 'greater', 'less'

    Returns
    -------
    z_stat, p_value : float
    """
    data = np.array(data)
    n = len(data)
    x_bar = np.mean(data)
    se = sigma / np.sqrt(n)

    # 検定統計量
    z_stat = (x_bar - mu_0) / se

    # p値
    if alternative == 'two-sided':
        p_value = 2 * (1 - stats.norm.cdf(np.abs(z_stat)))
    elif alternative == 'greater':
        p_value = 1 - stats.norm.cdf(z_stat)
    elif alternative == 'less':
        p_value = stats.norm.cdf(z_stat)

    return z_stat, p_value


# 使用例: 工場の製品長さ
np.random.seed(42)
data = np.random.normal(50.8, 2, 25)

z_stat, p_value = z_test_1sample(data, mu_0=50, sigma=2, alternative='two-sided')

print("=== 1標本z検定 ===")
print(f"標本平均: {np.mean(data):.4f}")
print(f"検定統計量: Z = {z_stat:.4f}")
print(f"p値: {p_value:.4f}")
print(f"α = 0.05 での判定: {'棄却' if p_value < 0.05 else '棄却しない'}")

検定結果の可視化

import numpy as np
import matplotlib.pyplot as plt
from scipy import stats

def plot_z_test(z_obs, alpha=0.05, alternative='two-sided'):
    """z検定の結果を可視化"""
    x = np.linspace(-4, 4, 1000)
    pdf = stats.norm.pdf(x)

    fig, ax = plt.subplots(figsize=(10, 6))
    ax.plot(x, pdf, 'k-', linewidth=2, label='$N(0, 1)$')

    if alternative == 'two-sided':
        z_crit = stats.norm.ppf(1 - alpha / 2)
        p_value = 2 * (1 - stats.norm.cdf(np.abs(z_obs)))

        # 棄却域(両側)
        x_right = x[x >= z_crit]
        ax.fill_between(x_right, stats.norm.pdf(x_right),
                        color='red', alpha=0.3, label=f'Rejection ($\\alpha/2$ = {alpha/2})')
        x_left = x[x <= -z_crit]
        ax.fill_between(x_left, stats.norm.pdf(x_left), color='red', alpha=0.3)

        ax.axvline(z_crit, color='red', linestyle='--', linewidth=1.5,
                   label=f'$\\pm z_{{\\alpha/2}}$ = $\\pm${z_crit:.3f}')
        ax.axvline(-z_crit, color='red', linestyle='--', linewidth=1.5)

    elif alternative == 'greater':
        z_crit = stats.norm.ppf(1 - alpha)
        p_value = 1 - stats.norm.cdf(z_obs)

        x_right = x[x >= z_crit]
        ax.fill_between(x_right, stats.norm.pdf(x_right),
                        color='red', alpha=0.3, label=f'Rejection ($\\alpha$ = {alpha})')
        ax.axvline(z_crit, color='red', linestyle='--', linewidth=1.5,
                   label=f'$z_\\alpha$ = {z_crit:.3f}')

    elif alternative == 'less':
        z_crit = stats.norm.ppf(alpha)
        p_value = stats.norm.cdf(z_obs)

        x_left = x[x <= z_crit]
        ax.fill_between(x_left, stats.norm.pdf(x_left),
                        color='red', alpha=0.3, label=f'Rejection ($\\alpha$ = {alpha})')
        ax.axvline(z_crit, color='red', linestyle='--', linewidth=1.5,
                   label=f'$-z_\\alpha$ = {z_crit:.3f}')

    # 観測された検定統計量
    ax.axvline(z_obs, color='blue', linewidth=2.5,
               label=f'$z_{{obs}}$ = {z_obs:.3f} (p = {p_value:.4f})')

    ax.set_xlabel('$z$', fontsize=13)
    ax.set_ylabel('Probability Density', fontsize=13)
    ax.set_title(f'Z-Test ({alternative}, $\\alpha$ = {alpha})', fontsize=14)
    ax.legend(fontsize=10, loc='upper left')
    ax.set_ylim(bottom=0)
    ax.grid(True, alpha=0.3)
    plt.tight_layout()
    plt.show()


# 両側検定の可視化
plot_z_test(z_obs=2.0, alpha=0.05, alternative='two-sided')

# 片側検定(右側)の可視化
plot_z_test(z_obs=1.8, alpha=0.05, alternative='greater')

2標本z検定の実装

import numpy as np
from scipy import stats

def z_test_2sample(data1, data2, sigma1, sigma2, alternative='two-sided'):
    """
    2標本z検定

    Parameters
    ----------
    data1, data2 : array-like
        各群の標本データ
    sigma1, sigma2 : float
        各群の母標準偏差(既知)
    alternative : str
        'two-sided', 'greater', 'less'

    Returns
    -------
    z_stat, p_value : float
    """
    data1, data2 = np.array(data1), np.array(data2)
    n1, n2 = len(data1), len(data2)

    se = np.sqrt(sigma1**2 / n1 + sigma2**2 / n2)
    z_stat = (np.mean(data1) - np.mean(data2)) / se

    if alternative == 'two-sided':
        p_value = 2 * (1 - stats.norm.cdf(np.abs(z_stat)))
    elif alternative == 'greater':
        p_value = 1 - stats.norm.cdf(z_stat)
    elif alternative == 'less':
        p_value = stats.norm.cdf(z_stat)

    return z_stat, p_value


# 使用例: 2工場の製品重量の比較
np.random.seed(42)
data_a = np.random.normal(102.5, 5.0, 36)
data_b = np.random.normal(100.3, 4.5, 40)

z_stat, p_value = z_test_2sample(data_a, data_b, sigma1=5.0, sigma2=4.5)

print("=== 2標本z検定 ===")
print(f"群A: n={len(data_a)}, x̄={np.mean(data_a):.4f}")
print(f"群B: n={len(data_b)}, x̄={np.mean(data_b):.4f}")
print(f"検定統計量: Z = {z_stat:.4f}")
print(f"p値: {p_value:.4f}")
print(f"α = 0.05 での判定: {'棄却' if p_value < 0.05 else '棄却しない'}")

まとめ

本記事では、z検定の理論と実装について解説しました。

  • 検定統計量: $Z = \frac{\bar{X} – \mu_0}{\sigma / \sqrt{n}} \sim N(0, 1)$($H_0$ のもと)
  • 前提条件: 母分散 $\sigma^2$ が既知であること
  • 棄却域: 両側検定では $|Z| > z_{\alpha/2}$、片側検定では $Z > z_\alpha$ または $Z < -z_\alpha$
  • p値: 帰無仮説のもとで観測値以上に極端な値が得られる確率
  • 2標本z検定: $Z = \frac{\bar{X} – \bar{Y}}{\sqrt{\sigma_1^2/n_1 + \sigma_2^2/n_2}}$

次のステップとして、以下の記事も参考にしてください。