はじめに
データ分析では、次のような結果をよく見かけます。
標本平均は47.6、母平均の95%信頼区間は43.4〜51.8だった。
ここで、つい次のように読みたくなります。
「母平均が43.4〜51.8に入る確率は95%である」
しかし、通常の頻度論的な95%信頼区間の意味は、これとは少し違います。
同じ方法で標本抽出と区間計算を何度も繰り返すと、作られた区間の約95%が、固定された真の母数を含む。
この記事では、母集団、標本、母数、標準誤差といった用語から始め、ダミーデータとPythonシミュレーションを使って、この意味を図で確認します。
使用するコードはGoogle Colabで上から順に実行できます。
TL;DR
- 母平均 $\mu$ は、頻度論では固定された未知の値として扱う
- 標本が変わると、標本平均と信頼区間は変わる
- 95%信頼区間の「95%」は、区間を作る手続きの長期的な被覆率を表す
- 今回得られた1本の区間について、$\mu$ が入る確率を95%としたものではない
- 標本サイズを増やすと、同じ95%信頼水準でも区間は狭くなる
今回の設定
説明を分かりやすくするため、次のダミー母集団を考えます。
$$
X_i \sim N(\mu=50,\ \sigma=10)
$$
これは、各データ $X_i$ が、母平均 $\mu=50$、母標準偏差 $\sigma=10$ の正規分布から独立に得られる、という意味です。
1回の標本抽出では、この母集団から $n=25$ 個のデータを取り出します。
シミュレーションなので真の母平均が50だと分かっていますが、実際の研究では、この値が分からないため標本から推定します。
また、データ生成時には $\sigma=10$ と設定していますが、信頼区間の計算では母標準偏差を未知として、標本標準偏差 $s$ を使います。そのため、今回はStudentのt分布を使った信頼区間を計算します。
まず用語を整理する
| 用語 | 意味 | 今回の例 |
|---|---|---|
| 母集団 | 知りたい対象全体、またはそれを表す確率分布 | 平均50、標準偏差10の正規分布 |
| 母数 | 母集団の性質を表す固定値 | 母平均 $\mu=50$ |
| 標本 | 母集団から実際に取り出したデータ | 25個のダミーデータ |
| 統計量 | 標本から計算した値 | 標本平均 $\bar{x}$、標本標準偏差 $s$ |
| 推定値 | 未知の母数を推定するための統計量の実現値 | 今回得られた標本平均 |
| 標準偏差 | 個々のデータのばらつき | $s$ |
| 標準誤差 | 推定量が標本抽出ごとにどれくらい変動するかを表す量 | $s/\sqrt{n}$ |
| 信頼区間 | 母数の候補範囲を、所定の手続きで計算した区間 | 母平均の95%信頼区間 |
| 信頼水準 | 区間を作る手続きの長期的な被覆率 | 95% |
標準偏差と標準誤差は違う
標本標準偏差 $s$ は、個々の観測値のばらつきを表します。
一方、標本平均の標準誤差は、標本を取り直したときに標本平均がどれくらい変動するかを表します。母標準偏差が未知のときは、次のように推定します。
$$
SE(\bar{x}) = \frac{s}{\sqrt{n}}
$$
標本サイズ $n$ が大きくなると分母の $\sqrt{n}$ が大きくなるため、標本平均の標準誤差は小さくなります。
母平均の95%信頼区間
正規母集団から独立に標本を取り、母標準偏差が未知である場合、母平均の95%信頼区間は次の式で計算できます。
$$
\bar{x}
\pm
t_{0.975,\ n-1}
\frac{s}{\sqrt{n}}
$$
それぞれの記号は次を表します。
- $\bar{x}$:標本平均
- $s$:標本標準偏差
- $n$:標本サイズ
- $s/\sqrt{n}$:標本平均の標準誤差
- $t_{0.975,\ n-1}$:自由度 $n-1$ のt分布の97.5パーセンタイル
95%信頼区間では、区間の外側に残る確率5%を左右に2.5%ずつ分けます。そのため、上側の臨界値として0.975点を使います。
今回の $n=25$ では自由度は24で、t臨界値は約2.064です。
「95%」は何にかかっているのか
ここがこの記事の中心です。
誤解しやすい表現
「今回得た区間に、母平均 $\mu$ が95%の確率で入っている」
頻度論的な意味
「同じ母集団から標本を取り、同じ式で区間を作ることを繰り返すと、作られた区間の約95%が母平均 $\mu$ を含む」
式で書くと、信頼区間を作る前には、次の被覆確率を持つように手続きが設計されています。
$$
P_{\mu}\left(L(X) \leq \mu \leq U(X)\right)=0.95
$$
ここで、$X$ はまだ観測していないランダムな標本、$L(X)$ と $U(X)$ はその標本から計算される信頼区間の下限と上限です。
標本を観測した後は、$L$ と $U$ は具体的な数値になります。頻度論では $\mu$ も固定値として扱うため、その区間が $\mu$ を含むかどうかは、すでに決まっています。
つまり、
95%は、今回の1本の区間に貼られた「確率ラベル」ではなく、区間を作る方法の長期的な成功率です。
これをPythonで確かめます。
Google Colabで実行する
1. ライブラリと共通関数を準備する
from pathlib import Path
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
from scipy import stats
# 図の保存先
FIG_DIR = Path("ci_figures")
FIG_DIR.mkdir(exist_ok=True)
# ダミー母集団と解析条件
MU = 50.0
SIGMA = 10.0
N = 25
CONFIDENCE = 0.95
def t_confidence_interval(data, confidence=0.95):
"""1標本t信頼区間を計算する。"""
data = np.asarray(data, dtype=float)
n = data.size
mean = data.mean()
sample_sd = data.std(ddof=1)
standard_error = sample_sd / np.sqrt(n)
alpha = 1.0 - confidence
t_critical = stats.t.ppf(
1.0 - alpha / 2.0,
df=n - 1
)
margin = t_critical * standard_error
lower = mean - margin
upper = mean + margin
return mean, lower, upper, sample_sd, standard_error, t_critical
def simulate_t_intervals(
rng,
mu,
sigma,
n,
repeats,
confidence=0.95
):
"""標本抽出と1標本t信頼区間の計算を繰り返す。"""
samples = rng.normal(
loc=mu,
scale=sigma,
size=(repeats, n)
)
means = samples.mean(axis=1)
sample_sds = samples.std(axis=1, ddof=1)
standard_errors = sample_sds / np.sqrt(n)
alpha = 1.0 - confidence
t_critical = stats.t.ppf(
1.0 - alpha / 2.0,
df=n - 1
)
margins = t_critical * standard_errors
lower = means - margins
upper = means + margins
contains_true_mean = (
(lower <= mu) & (mu <= upper)
)
return means, lower, upper, contains_true_mean
print(f"Figure directory: {FIG_DIR.resolve()}")
2. まず1回だけ標本を取り出す
rng_one = np.random.default_rng(20260618)
sample = rng_one.normal(
loc=MU,
scale=SIGMA,
size=N
)
(
sample_mean,
ci_lower,
ci_upper,
sample_sd,
standard_error,
t_critical
) = t_confidence_interval(
sample,
confidence=CONFIDENCE
)
print("One sample")
print(f"n = {N}")
print(f"sample mean = {sample_mean:.3f}")
print(f"sample SD = {sample_sd:.3f}")
print(f"standard error = {standard_error:.3f}")
print(f"t critical value = {t_critical:.3f}")
print(f"95% CI = [{ci_lower:.3f}, {ci_upper:.3f}]")
print(
"contains true mean? "
f"{ci_lower <= MU <= ci_upper}"
)
# Figure 1: 母集団分布と1回の標本
x = np.linspace(
MU - 4 * SIGMA,
MU + 4 * SIGMA,
500
)
density = stats.norm.pdf(
x,
loc=MU,
scale=SIGMA
)
fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(
x,
density,
linewidth=2,
label="Population distribution"
)
ax.fill_between(x, density, alpha=0.15)
ax.scatter(
sample,
np.full_like(sample, -0.0015),
marker="|",
s=180,
linewidths=1.8,
label="One sample (n=25)"
)
ax.axvline(
MU,
linestyle="--",
linewidth=2,
label=f"True population mean = {MU:.1f}"
)
ax.axvline(
sample_mean,
linestyle=":",
linewidth=2,
label=f"Sample mean = {sample_mean:.2f}"
)
ax.set_xlabel("Value")
ax.set_ylabel("Probability density")
ax.set_title(
"Population, one random sample, and two means"
)
ax.set_ylim(-0.004, density.max() * 1.12)
ax.grid(alpha=0.25)
ax.legend()
fig.tight_layout()
fig.savefig(
FIG_DIR / "fig1_population_and_sample.png",
dpi=200,
bbox_inches="tight"
)
plt.show()
# Figure 2: 1回の標本から得た95%信頼区間
fig, ax = plt.subplots(figsize=(9, 3.2))
ax.errorbar(
sample_mean,
0,
xerr=[
[sample_mean - ci_lower],
[ci_upper - sample_mean]
],
fmt="o",
markersize=8,
capsize=8,
linewidth=2,
label="Sample mean and 95% CI"
)
ax.axvline(
MU,
linestyle="--",
linewidth=2,
label=f"True population mean = {MU:.1f}"
)
ax.set_yticks([])
ax.set_xlabel("Population mean")
ax.set_title(
"A 95% confidence interval from one sample"
)
ax.grid(axis="x", alpha=0.25)
ax.legend(loc="upper left")
fig.tight_layout()
fig.savefig(
FIG_DIR / "fig2_one_confidence_interval.png",
dpi=200,
bbox_inches="tight"
)
plt.show()
次の結果がでます。
One sample
n = 25
sample mean = 47.587
sample SD = 10.240
standard error = 2.048
t critical value = 2.064
95% CI = [43.360, 51.814]
contains true mean? True
図1では、破線が真の母平均50、点線が今回の標本平均47.59です。
25個の標本は母集団からランダムに選ばれるため、標本平均は真の母平均と完全には一致しません。別の標本を取れば、標本平均も別の値になります。
図2の点は標本平均47.59、横線は95%信頼区間43.36〜51.81です。今回は真の母平均50を含みました。
ただし、この1本だけを見ても「95%」の意味は分かりません。信頼水準は、標本抽出を繰り返したときに現れる性質だからです。
3. 標本抽出と区間計算を100回繰り返す
次に、同じ母集団から25個のデータを取り出して95%信頼区間を作る操作を、独立に100回繰り返します。
rng_100 = np.random.default_rng(20260620)
(
means_100,
lower_100,
upper_100,
contains_100
) = simulate_t_intervals(
rng=rng_100,
mu=MU,
sigma=SIGMA,
n=N,
repeats=100,
confidence=CONFIDENCE
)
print("100 repeated samples")
print(
"intervals containing the true mean = "
f"{contains_100.sum()} / 100"
)
print(
"observed coverage rate = "
f"{contains_100.mean():.3f}"
)
fig, ax = plt.subplots(figsize=(9, 11))
y = np.arange(1, 101)
for i in range(100):
line_color = "C0" if contains_100[i] else "C3"
ax.hlines(
y=y[i],
xmin=lower_100[i],
xmax=upper_100[i],
color=line_color,
linewidth=2
)
ax.plot(
means_100[i],
y[i],
"o",
color=line_color,
markersize=3.5
)
ax.axvline(
MU,
color="black",
linestyle="--",
linewidth=2,
label=f"True population mean = {MU:.1f}"
)
ax.set_xlabel(
"Confidence interval for the population mean"
)
ax.set_ylabel("Repeated sample number")
ax.set_title(
"100 independently constructed "
"95% confidence intervals"
)
ax.set_yticks(np.arange(10, 101, 10))
ax.invert_yaxis()
ax.grid(axis="x", alpha=0.25)
legend_items = [
Line2D(
[0], [0],
color="C0",
linewidth=2,
marker="o",
label="Contains the true mean"
),
Line2D(
[0], [0],
color="C3",
linewidth=2,
marker="o",
label="Misses the true mean"
),
Line2D(
[0], [0],
color="black",
linewidth=2,
linestyle="--",
label="True population mean"
)
]
ax.legend(
handles=legend_items,
loc="lower right"
)
fig.tight_layout()
fig.savefig(
FIG_DIR / "fig3_repeated_confidence_intervals.png",
dpi=200,
bbox_inches="tight"
)
plt.show()
次の結果になります。
100 repeated samples
intervals containing the true mean = 94 / 100
observed coverage rate = 0.940
図3では、
- 青い区間:真の母平均50を含んだ
- 赤い区間:真の母平均50を含まなかった
- 黒い破線:固定された真の母平均50
を表しています。
標本を取り直すたびに、標本平均も信頼区間も動いています。一方、真の母平均50は動きません。
今回は100本のうち94本が真の母平均を含み、6本は含みませんでした。
95%信頼区間だからといって、100回ごとに必ず95本が成功するわけではありません。コインを100回投げても表が必ず50回になるとは限らないのと同じで、有限回の結果には偶然の変動があります。
被覆率は次のように計算できます。
$$
\text{被覆率}=
\frac{\text{真の母平均を含んだ区間数}}
{\text{作成した区間数}}
$$
4. 1万回繰り返して長期的な被覆率を見る
100回では偶然の変動がまだ目立ちます。そこで、同じ操作を1万回繰り返します。
REPEATS = 10_000
rng_many = np.random.default_rng(20260621)
(
means_many,
lower_many,
upper_many,
contains_many
) = simulate_t_intervals(
rng=rng_many,
mu=MU,
sigma=SIGMA,
n=N,
repeats=REPEATS,
confidence=CONFIDENCE
)
cumulative_coverage = (
np.cumsum(contains_many)
/ np.arange(1, REPEATS + 1)
)
final_coverage = contains_many.mean()
print("10,000 repeated samples")
print(
"intervals containing the true mean = "
f"{contains_many.sum()} / {REPEATS}"
)
print(
"observed coverage rate = "
f"{final_coverage:.4f}"
)
fig, ax = plt.subplots(figsize=(10, 5))
repetition_numbers = np.arange(1, REPEATS + 1)
ax.plot(
repetition_numbers,
cumulative_coverage,
linewidth=1.5,
label="Cumulative observed coverage"
)
ax.axhline(
CONFIDENCE,
color="black",
linestyle="--",
linewidth=2,
label="Nominal confidence level = 0.95"
)
ax.scatter(
[REPEATS],
[final_coverage],
s=55,
zorder=3
)
ax.annotate(
f"Final coverage = {final_coverage:.4f}",
xy=(REPEATS, final_coverage),
xytext=(-170, 28),
textcoords="offset points",
arrowprops={"arrowstyle": "->"}
)
ax.set_xlabel("Number of repeated samples")
ax.set_ylabel("Cumulative coverage rate")
ax.set_title(
"The long-run coverage approaches 95%"
)
ax.set_ylim(0.85, 1.005)
ax.grid(alpha=0.25)
ax.legend()
fig.tight_layout()
fig.savefig(
FIG_DIR / "fig4_cumulative_coverage.png",
dpi=200,
bbox_inches="tight"
)
plt.show()
次の結果になります。
10,000 repeated samples
intervals containing the true mean = 9509 / 10000
observed coverage rate = 0.9509
最初のうちは、累積被覆率が大きく揺れます。しかし、繰り返し回数が増えるにつれて0.95付近に落ち着きました。
今回の1万本では、9509本が真の母平均を含み、観測された被覆率は0.9509でした。
これが95%信頼区間の「95%」をシミュレーションで見た結果です。
前提が満たされているとき、同じ方法で作った区間の長期的な約95%が真の母平均を含みます。
おまけ:標本サイズを増やすとどうなるか
信頼水準を95%に固定したまま、標本サイズ $n$ を変えてみます。
rng_n = np.random.default_rng(20260622)
sample_sizes = [5, 10, 25, 100]
records = []
for n in sample_sizes:
(
means_n,
lower_n,
upper_n,
contains_n
) = simulate_t_intervals(
rng=rng_n,
mu=MU,
sigma=SIGMA,
n=n,
repeats=20_000,
confidence=CONFIDENCE
)
records.append({
"n": n,
"coverage_rate": contains_n.mean(),
"average_interval_width": np.mean(
upper_n - lower_n
)
})
result_by_n = pd.DataFrame(records)
print(
result_by_n.to_string(
index=False,
float_format=lambda value: f"{value:.4f}"
)
)
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(
result_by_n["n"],
result_by_n["average_interval_width"],
marker="o",
linewidth=2
)
ax.set_xlabel("Sample size n")
ax.set_ylabel("Average width of 95% CI")
ax.set_title(
"Larger samples produce narrower "
"confidence intervals"
)
ax.grid(alpha=0.25)
fig.tight_layout()
fig.savefig(
FIG_DIR / "fig5_interval_width_by_sample_size.png",
dpi=200,
bbox_inches="tight"
)
plt.show()
次の結果になります。
n coverage_rate average_interval_width
5 0.9480 23.3422
10 0.9516 13.9211
25 0.9520 8.1895
100 0.9486 3.9603
どの標本サイズでも、被覆率はおおむね0.95です。一方、平均的な区間幅は、$n$ が大きくなるほど狭くなりました。
ここで大切なのは、標本サイズを増やしたから信頼水準が95%から上がったのではない、という点です。
- 信頼水準:95%のまま
- 標本サイズ:増加
- 標準誤差:減少
- 信頼区間:狭くなり、推定精度が上がる
という関係です。
よくある誤解
誤解1:信頼区間の中にデータの95%が入る
違います。
この記事で計算しているのは、母平均に対する信頼区間です。個々の観測値の95%が入る範囲ではありません。
個々の将来観測値を対象にする区間や、母集団の一定割合を含む区間は、別の目的と計算方法を持ちます。
誤解2:100回試せば必ず95回は真値を含む
違います。
95%は長期的な割合です。100回程度では、94回や97回などに変動します。
誤解3:今回の区間に真値が入る確率が95%である
通常の頻度論的な信頼区間では、そのような確率を今回の固定された区間に割り当てていません。
今回の区間は、真の母平均を含んでいるか、含んでいないかのどちらかです。ただし、実データでは真の母平均が分からないため、どちらであるかを直接確認できません。
誤解4:区間が狭ければ狭いほどよい
区間が狭いことは、通常は推定精度が高いことを示します。ただし、前提が崩れた方法で無理に狭い区間を作っても、真値を所定の割合で含まなければ意味がありません。
区間幅と被覆率の両方を見ることが重要です。
ベイズ信用区間との違い
ベイズ統計では、事前分布と確率モデルを定め、データを観測した後の事後分布を使います。
そのため、95%ベイズ信用区間は、設定したモデルと事前分布のもとで、次のような事後確率の意味を持たせられます。
$$
P\left(\mu \in [a,b]\mid \text{data}\right)=0.95
$$
これは、この記事で扱った頻度論的な95%信頼区間とは別の考え方です。
「信頼区間」と「信用区間」は似た名前ですが、確率の解釈が異なります。
このシミュレーションの前提と注意点
今回の結果は、次の条件に基づいています。
- 各データが互いに独立である
- データが正規分布から生成される
- 母平均を1標本t信頼区間で推定する
- データ生成過程と解析方法が適合している
この条件では、t信頼区間は理論上95%の被覆率を持ちます。
実データでは、次のような問題があり得ます。
- 強い歪み
- 外れ値
- 時系列相関や個体内相関
- 擬似反復
- 選択バイアス
- 欠測
- 測定誤差
- 解析後に都合のよい条件だけを選ぶこと
そのため、どのようなデータにも機械的に同じ信頼区間を適用すればよいわけではありません。実験計画、データの生成過程、独立性、分布の形を確認する必要があります。
試してみる課題
コード中の値を変更すると、理解がさらに深まります。
-
N = 5やN = 100に変え、1本の信頼区間の幅を比較する -
CONFIDENCE = 0.90や0.99に変え、被覆率と区間幅を比較する - 100回シミュレーションの乱数seedを変え、成功本数が毎回変わることを確認する
- 繰り返し回数を100、1,000、10,000と変え、累積被覆率の揺れを比べる
- 正規分布以外の分布からデータを生成し、同じt信頼区間の被覆率がどう変わるか調べる
まとめ
- 母平均 $\mu$ は固定され、標本と信頼区間が標本抽出ごとに変化する
- 95%信頼区間は、同じ手続きを繰り返したときに約95%の区間が真の母平均を含むように作られている
- 「今回の区間に真値が入る確率が95%」という意味ではない
- 100回のうち必ず95回成功するのではなく、有限回では偶然に揺れる
- 標本サイズを増やすと、同じ95%信頼水準でも信頼区間は狭くなる
- 信頼区間の解釈は、データと計算方法の前提が満たされていることを必要とする
一文で言い換えるなら、次のようになります。
95%信頼区間の95%は、「今回の真値の確率」ではなく、「区間を作る方法の長期的な被覆性能」である。



