母比率の信頼区間(正規分布へ近似を利用するもの)
これは何か
- 母比率の信頼区間について調べたことをまとめたもの
- 信頼区間構築のやり方はいくつもあるが, 本記事では正規分布への近似を利用するものについてまとめた
- 具体的には, 以下の3つの信頼区間について整理した
- Wald interval
- Wilson interval
- Agresti–Coull interval
- 具体的には, 以下の3つの信頼区間について整理した
記号の定義
- Binom
: 2項分布(n, p) -
: 標準正規分布の上側z_{\alpha} %点100 \alpha - 本記事では表記簡略化のため、信頼区間は
の形で表記CI = c \pm d -
を意味しているCI = [c - d, c + d]
-
各信頼区間の概要
以後,
Wald interval
- 2項分布の正規分布近似から導かれる信頼区間
- 入門的な統計学のテキストで頻繁に見かける
Wald intervalの導出
まず, 中心極限定理より,
信頼区間は, 帰無仮説
この不等式を
この考え方自体は理論的に正当化できるが, 本題から外れるため, 補足情報として触れるにとどめておく.
(補足)
示すべきことは, 分母のパラメータを置き換えた統計量
まず, 注目している統計量を
ここで本題に戻る. 「分母に含まれる母比率パラメータ
これを
となり, Wald interval
が得られる.
Wilson interval
- Wald intervalの導出の過程で出てくる不等式をそのまま解くことで得られる信頼区間
- 一見すると複雑な式だが, 区間の中央の値は
と\hat{p} の加重平均となっている\frac{1}{2} \frac{\hat{p} + \frac{z_{\alpha/2}^{2}}{2 n}}{1 + \frac{z_{\alpha/2}^{2}}{n}} = \frac{n}{n + z_{\alpha/2}^{2}} \hat{p} + \frac{z_{\alpha/2}^{2}}{n + z_{\alpha/2}^{2}} \frac{1}{2} - Wald intervalの中央の値は
なので, Wilson intervalの中央の値はそれと比べて\hat{p} に寄った形となるp = \frac{1}{2}
Wilson intervalの導出
基本的な考え方はWald intervalと同じだが, 途中で出てきた以下の不等式をそのまま解くのが異なる点である:
両辺を2乗して分母を払うと
となり,
という
等号が成立する
となる.
以上よりWilson interval
が得られる.
Agresti–Coull interval
- Wald intervalをベースに
を補正して得られる信頼区間n, X, \hat{p} - 区間の中央の値がWilson intervalの中央の値と同じになるように補正
ただし,
\tilde{n} = n + z_{\alpha/2}^{2} \tilde{X} = X + \frac{z_{\alpha/2}^{2}}{2} -
.\tilde{p} = \frac{\tilde{X}}{\tilde{n}}
Agresti–Coull intervalの導出
Wald intervalは
であった. 式の形自体はシンプルではあるので, この式の形を維持しつつ,
1つの考え方として, 中央の値がWilson intervalの中央の値
ここで, やや天下り的ではあるが
\tilde{n} = n + z_{\alpha/2}^{2} \tilde{X} = X + \frac{z_{\alpha/2}^{2}}{2} \tilde{p} = \frac{\tilde{X}}{\tilde{n}}
のように定義してみる. すると,
この
信頼区間の観察
信頼区間の数値例
実際に
(可視化コード)
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm
def wald_confidence_interval(n: int, x: int, alpha: float = 0.05) -> tuple[float, float]:
z = norm.ppf(1 - alpha / 2)
hat_p = x / n
deviation = z * np.sqrt(hat_p * (1 - hat_p) / n)
lower = hat_p - deviation
upper = hat_p + deviation
return (lower, upper)
def wilson_confidence_interval(n: int, x: int, alpha: float = 0.05) -> tuple[float, float]:
z = norm.ppf(1 - alpha / 2)
hat_p = x / n
center = hat_p + (z**2) / (2*n)
deviation = z / np.sqrt(n) * np.sqrt(hat_p * (1 - hat_p) + (z**2) / (4*n))
denominator = 1 + (z**2)/n
lower = (center - deviation) / denominator
upper = (center + deviation) / denominator
return (lower, upper)
def agresti_coull_confidence_interval(n: int, x: int, alpha: float = 0.05) -> tuple[float, float]:
z = norm.ppf(1 - alpha / 2)
tilde_n = n + z**2
tilde_x = x + (z**2)/2
tilde_p = tilde_x / tilde_n
deviation = z * np.sqrt(tilde_p * (1 - tilde_p) / tilde_n)
lower = tilde_p - deviation
upper = tilde_p + deviation
return (lower, upper)
def main():
n = 20
alpha = 0.05
xs = np.arange(n + 1)
wald_lower, wald_upper = np.zeros(n + 1), np.zeros(n + 1)
wilson_lower, wilson_upper = np.zeros(n + 1), np.zeros(n + 1)
agresti_coull_lower, agresti_coull_upper = np.zeros(n + 1), np.zeros(n + 1)
for x in xs:
wald_ci = wald_confidence_interval(n, x, alpha)
wald_lower[x], wald_upper[x] = wald_ci[0], wald_ci[1]
wilson_ci = wilson_confidence_interval(n, x, alpha)
wilson_lower[x], wilson_upper[x] = wilson_ci[0], wilson_ci[1]
agresti_coull_ci = agresti_coull_confidence_interval(n, x, alpha)
agresti_coull_lower[x], agresti_coull_upper[x] = agresti_coull_ci[0], agresti_coull_ci[1]
plt.plot(xs, np.zeros(len(xs)), linestyle="-", color="black")
plt.plot(xs, np.ones(len(xs)), linestyle="-", color="black")
plt.plot(xs, wald_lower, label="wald", linestyle=":", marker="o", color="blue", markerfacecolor='none')
plt.plot(xs, wald_upper, linestyle=":", marker="o", color="blue", markerfacecolor='none')
plt.plot(xs, wilson_lower, label="wilson", linestyle=":", marker="x", color="red")
plt.plot(xs, wilson_upper, linestyle=":", marker="x", color="red")
plt.plot(xs, agresti_coull_lower, label="agresti-coull", linestyle=":", marker="+", color="green")
plt.plot(xs, agresti_coull_upper, linestyle=":", marker="+", color="green")
plt.xlabel("$x$")
plt.ylabel("$p$")
plt.title(f"Confidence Intervals (n={n} / confidence level={1-alpha})")
plt.legend(loc="upper left")
plt.show()
if __name__ == "__main__":
main()
横軸を
すると, グラフから以下のことが読み取れる:
- Wald interval は
において信頼区間が1点のみになるX = 0, n - Wilson interval は0以上1以下の範囲に収まっている
- 逆に, Wald interval・Agresti–Coull interval は0未満・1より大きい範囲を含むことがある
- Agresti–Coull interval は Wilson interval を含む
これらの性質が成り立つことは比較的簡単な計算で確認ができるため, 後ほど確認していく.
信頼区間の被覆確率
1つ前のセクションでは, 信頼区間の実現値の挙動について確認をした. その際, 「真の母比率」が信頼区間の実現値に含まれるか, といった議論はしていなかった. そこで本セクションでは, (実世界が2項分布で表現できる前提のもとでの)真の母比率と信頼区間の関係性について確認する.
被覆確率を「2項分布モデルのもとで, (確率変数としての)信頼区間が真の母比率を含む確率」と定義し, その値がどうなるかを調べてみる. 被覆確率を数式で表現すると以下のようになる(被覆確率は, 2項分布のパラメータ
ここで, 3点補足を付け加えておく:
- 被覆確率の高さだけで信頼区間の良し悪しを論じるのはNG
- 極端な話, 常に信頼区間を
としておけば被覆確率は1(100%)になるが, 「母比率の信頼区間は\left[ 0, 1 \right] です」と言われても何の情報も得られない\left[ 0, 1 \right] - 信頼区間の幅など, 他の観点も併せて評価する必要がある
- 極端な話, 常に信頼区間を
- ある程度妥当性を感じられる被覆確率の値は,
(1-\alpha %)以上100(1-\alpha) - Wald interval の議論を思い出すと, 本記事で扱った信頼区間はいずれも「信頼水準
%の両側検定から信頼区間を構築する」というアイデアに基づいている100(1-\alpha) - 仮説検定の観点から見ると, 第1種の過誤を
(\alpha %)以下に抑えることを期待している100 \alpha - これを被覆確率の観点で言い換えれば, 被覆確率が
(1-\alpha %)以上になることを期待している, ということになる100(1-\alpha)
- Wald interval の議論を思い出すと, 本記事で扱った信頼区間はいずれも「信頼水準
- 確率変数としての信頼区間と, 実際のデータから計算した実現値としての信頼区間の区別は必要
- 被覆確率の議論では, 確率変数としての信頼区間を念頭においている
- 実際のデータから計算した実現値としての信頼区間の場合, その区間が真の母比率を含むか否かはデータを取った時点で確定しているため, 「その区間が真の母比率を含む確率」なるものを考えたところで0 or 1にしかならない
実際に
(可視化コード)
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm, binom
def wald_confidence_interval(n: int, x: int, alpha: float = 0.05) -> tuple[float, float]:
z = norm.ppf(1 - alpha / 2)
hat_p = x / n
deviation = z * np.sqrt(hat_p * (1 - hat_p) / n)
lower = hat_p - deviation
upper = hat_p + deviation
return (lower, upper)
def wilson_confidence_interval(n: int, x: int, alpha: float = 0.05) -> tuple[float, float]:
z = norm.ppf(1 - alpha / 2)
hat_p = x / n
center = hat_p + (z**2) / (2*n)
deviation = z / np.sqrt(n) * np.sqrt(hat_p * (1 - hat_p) + (z**2) / (4*n))
denominator = 1 + (z**2)/n
lower = (center - deviation) / denominator
upper = (center + deviation) / denominator
return (lower, upper)
def agresti_coull_confidence_interval(n: int, x: int, alpha: float = 0.05) -> tuple[float, float]:
z = norm.ppf(1 - alpha / 2)
tilde_n = n + z**2
tilde_x = x + (z**2)/2
tilde_p = tilde_x / tilde_n
deviation = z * np.sqrt(tilde_p * (1 - tilde_p) / tilde_n)
lower = tilde_p - deviation
upper = tilde_p + deviation
return (lower, upper)
def main():
n = 20
alpha = 0.05
ps = np.arange(0.02, 1, 0.02)
wald_coverage_probabilities = np.zeros(len(ps))
wilson_coverage_probabilies = np.zeros(len(ps))
agresti_coull_coverage_probabilies = np.zeros(len(ps))
for index, p in enumerate(ps):
for x in np.arange(n + 1):
px = binom.pmf(x, n, p)
wald_ci = wald_confidence_interval(n, x, alpha)
if wald_ci[0] <= p and p <= wald_ci[1]:
wald_coverage_probabilities[index] += px
wilson_ci = wilson_confidence_interval(n, x, alpha)
if wilson_ci[0] <= p and p <= wilson_ci[1]:
wilson_coverage_probabilies[index] += px
agresti_coull_ci = agresti_coull_confidence_interval(n, x, alpha)
if agresti_coull_ci[0] <= p and p <= agresti_coull_ci[1]:
agresti_coull_coverage_probabilies[index] += px
plt.plot(ps, (1 - alpha) * np.ones(len(ps)), linestyle="-", color="black")
plt.plot(ps, wald_coverage_probabilities, label="wald", linestyle=":", marker="o", color="blue", markerfacecolor='none')
plt.plot(ps, wilson_coverage_probabilies, label="wilson", linestyle=":", marker="x", color="red")
plt.plot(ps, agresti_coull_coverage_probabilies, label="agresti-coull", linestyle=":", marker="+", color="green")
plt.xlabel("$p$")
plt.ylabel("coverage probability")
plt.title(f"Coverage Probabilities (n={n} / confidence level={1-alpha})")
plt.legend(loc="lower center")
plt.show()
if __name__ == "__main__":
main()
横軸を真の母比率
グラフから読み取れることとしては以下のとおり:
- Wald interval の被覆確率は, 特に
の両端に近い部分で急激に低くなるp=0, 1 - Agresti-Coull interval の被覆確率が Wilson intervalの被覆確率以上となる
- これは先述した「Agresti-Coull interval は Wilson interval を含む」という事実から導かれる
信頼区間の特徴
X = 0, n において信頼区間が1点のみ
Wald intervalは- これが成り立つことは, Wald intervalの式に
を代入してみれば明らか\hat{p} = 0, 1 - 信頼区間が1点のみとなってしまう性質は, 望ましいとは言えない
- 例えば
だった場合, 信頼区間はn=10, X=0 の1点のみとなる0 - 薬の副作用を調べる際に「10人調べて副作用出た人が0人だったから, 副作用発生率の信頼区間は
です」と言われて納得できるだろうか?0
- 例えば
Wilson score intervalは0以上1以下の範囲に収まる
- 別途0, 1の境界値の処理を考慮する必要がないのは利点
- これが成り立つことは, 簡単な計算で確かめることができる
(証明はこちら)
- Wilson score intervalの下限が0以上
- Wilson score intervalの上限が1以下
の2点を示せばよい.
まずは前者(Wilson score intervalの下限が0以上)を示す. 示すべき式は
である. そこで, 左辺の2乗 - 右辺の2乗を計算すると
となり, 左辺
次に後者(Wilson score intervalの上限が1以下)を示す. 示すべき式は
である. そこで, 右辺の2乗 - 左辺の2乗を計算すると
となり, 左辺
(証明終わり)
Agresti–Coull interval は Wilson interval を含む
- ここから「Agresti–Coull interval の被覆確率は Wilson interval の被覆確率以上である」ことが言える
- ざっくりいえば, Agresti–Coull interval の方が Wilson interval より保守的な信頼区間だといえる
- これも, 簡単な計算で確かめることができる
(証明はこちら)
両信頼区間を再掲する:
両区間とも中央の値は同じため, 幅に相当する部分の大小関係によって信頼区間の包含関係が決まる. そこで
と置き,
2乗の差をとると
となる(
(証明終わり)
p=0, 1 の両端に近い部分で急激に低くなる
Wald interval の被覆確率は, 特にBrown et al.(2002)のsection 2で議論があるため, 詳細はそちらを参照. 論文中では, 本記事で紹介したもの以外のものも含めた複数の信頼区間の被覆確率をEdgeworth展開し, その結果に基づいた比較が行われている.
個人的な所感
- あえて Wald interval を推す理由はなさそう
- 理論的な正当化はできるが, 統計初学者にはハードルが高い
- 被覆確率の挙動も望ましくない(特に
周辺)p=0, 1 - とはいえ計算式は非常にシンプルなので, 統計学の試験など, 計算資源が乏しい中短時間で計算をしないといけないような限定的な場面であれば, 使う意義はありそう
- 実際に信頼区間を計算する際は, 以下のように使い分けをするとよさそう
- 基本的には Wilson interval を利用
- 3つの中で式は一番複雑に見えるが, 導出の過程は自然に感じられた
- 被覆確率を見てもさほど大きな問題はなく, 信頼区間が0以上1以下の範囲に収まる点も良い
- 簡便さを重視する場合は Agresti-Coull interval を利用
- Wald interval のような式の簡便さと被覆確率の安定度合いを両方兼ね備えている
- ただ, 信頼区間が0以上1以下の範囲に収まるとは限らず, 多少のケアは必要
- 基本的には Wilson interval を利用
- 母比率の信頼区間の使い分けについては Brown et al.(2001) の議論が参考になる
参考文献
- Brown, L. D., Cai, T. T., and DasGupta, A. (2001). Interval Estimation for a Binomial Proportion. Statistical Science, 16(2), 101-133.
- Brown, L. D., Cai, T. T., and DasGupta, A. (2002). Confidence Intervals for a binomial proportion and asymptotic expansions. The Annals of Statistics, 30(1), 160–201
Discussion