95% CI = [a, b] を、数式不要で求める。「bootstrap」は統計データ分析の文脈で扱う重要概念のひとつ。 本ページでは「bootstrap」を取り巻く中核キーワードを以下にチップで一覧化する。 各キーワードは関連する概念・手法・道具立てを含み、 文献検索や学習計画の起点になる。
これらのキーワードは「bootstrap の理解 → 適用 → 検証」のプロセスを構成する。 各章で詳しく解説する。
🍰 まずはやさしく
データのコピーをたくさん作る方法です。
結果がどれくらいブレるか知るために使います。
部活の平均点などのバラつきを調べます。
この方法で何がわかるかを解説します。
🍰 まずはやさしく
論文などでよく使われる計算手法です。
データの数が少ないときに役立ちます。
スマホの利用時間の調査などで使われます。
実際の表記がどうなっているかを見ます。
論文の表や Methods 節で、こんな表記を見たはずです:
この 「Bootstrap CI」「B=10,000」が、 ブートストラップ法(bootstrap, resampling) を使った結果です。 標本が小さい、 分布が非正規、 統計量が複雑 ── そんなときに「手持ちデータを母集団とみなして何千回もコピーを作り、 統計量のブレ幅を直接観測する」のがブートストラップの発想。
🍰 まずはやさしく
バケツから水を汲み直すような考え方です。
数式を使わずにデータの分布を調べます。
買い物での出費の傾向を調べるイメージです。
なぜこの方法で正解に近づくかを考えます。
古典的な推測統計(z 検定や t 検定)は、 こう考えます:
でも実際には、 (1) の仮定が成り立たない、 あるいは (2) の数式がそもそも存在しない統計量(中央値、 IQR、 トリム平均、 相関、 主成分の固有値...)が山ほどあります。 そこで Efron が 1979 年に提案したのが、 「データそのものを使って標本分布を真似(resample)する」 という大胆な発想です。
母集団=大きな池、 標本=池からすくった一杯のバケツ、 と考えます。 一杯しか汲めない(データ収集は高くつく)ので、 私たちはバケツの中身しか直接見られない。 でも:
つまりブートストラップは、 「標本 = ミニ母集団」というプラグイン原理(plug-in principle)を信じて、 統計量の「たられば」を計算機の中で再現する手法です。
n=5 の小さな標本 {3, 5, 7, 8, 12} から復元抽出すると:
{5, 5, 7, 12, 3} → 平均 = 6.4{8, 8, 8, 5, 7} → 平均 = 7.2{3, 12, 3, 7, 5} → 平均 = 6.0こうして得た B 個の平均値の 2.5% 点〜97.5% 点 が、 ノンパラメトリックな 95% 信頼区間。 これが percentile 法 の基本形です。
下は SSDSE-B-2026 の 2023 年・47 都道府県の合計特殊出生率(A4103) を実データとして使ったインタラクティブ実験です。 上段の点が元標本(47 県)。 「1 回リサンプル」で復元抽出の様子(選ばれた点が光り、 同じ点が複数回選ばれることもある)を観察し、 「1000 回ブートストラップ」でブートストラップ分布のヒストグラムが構築され、 パーセンタイル 95% 信頼区間(2.5%〜97.5%)が塗られます。 リサンプルサイズや回数を変えて、 信頼区間の幅がどう変わるかを体感してください。
※ 乱数を使うので実行のたびに CI はわずかに変動します。 参考:本ページの Python 実装(seed 固定 B=10000)では 平均 = 1.293、 95% percentile CI ≈ [1.255, 1.330]、 中央値 95% CI ≈ [1.23, 1.33]。 このシミュレータの結果もこの近傍に収束します。
scipy.stats.bootstrap の既定です。関連ページ:ここで得た区間そのものは 信頼区間、 分布の広がり(帯の幅の素)は 標準誤差。 「なぜ平均の分布が釣鐘型に近づくのか」は 標本分布と中心極限定理 が説明します。 ブートストラップは CLT が使えない統計量にも同じ枠組みを広げる一般化と捉えられます。
🍰 まずはやさしく
データを何度も選び直す手順のことです。
統計量(平均などの値)を正確に求めます。
テストの点数の分布をシミュレーションします。
具体的な計算の流れについて説明します。
元の標本を $\mathbf{X} = (X_1, X_2, \dots, X_n)$、 推定したい統計量を $\hat{\theta} = T(\mathbf{X})$ と書きます。 ブートストラップでは:
各 b = 1, 2, ..., B について統計量を再計算します:
パーセンタイル法は分布が歪んでいるとバイアスを生むため、 補正項を入れたのが BCa 法:
scipy.stats.bootstrap(..., method='BCa') で 1 行。| 手法 | CI 公式 | 長所 | 短所 | 推奨 |
|---|---|---|---|---|
| Percentile | $[\hat\theta^*_{(\alpha/2)}, \hat\theta^*_{(1-\alpha/2)}]$ | シンプル | 偏り補正なし | 初学者向け |
| Basic | $[2\hat\theta - \hat\theta^*_{(1-\alpha/2)}, 2\hat\theta - \hat\theta^*_{(\alpha/2)}]$ | 偏り部分補正 | 対称性に依存 | 中級 |
| Bootstrap-t | $\hat\theta \pm t^*_{(1-\alpha/2)} \cdot \hat{SE}^*$ | 高精度 | 計算重い | 理論派 |
| BCa | 加速度バイアス補正 | 最も正確 | 実装複雑 | 推奨デフォルト |
| ABC | 解析的版 BCa | 高速 BCa | 一階近似 | 研究用 |
BCa (Bias-Corrected and accelerated) は二階精度で、 平均値・中央値・分位点・相関のいずれにも適切な CI を与えます。 scipy.stats.bootstrap は BCa をデフォルトに採用しています。
💡 重要:ブートストラップは「母集団から繰り返し標本を取る」のではなく「標本から繰り返し復元抽出する」操作です。 つまり「標本 = 母集団の縮図」と信じるのがキモ。 この仮定が破れるとブートストラップも破綻する(後述の落とし穴)。
Hall (1992) の Edgeworth 解析によると、 Bootstrap CI の被覆精度は手法によって異なります:
| CI 手法 | 片側精度 | 両側精度 | 補正 |
|---|---|---|---|
| 通常正規 | $O(n^{-1/2})$ | $O(n^{-1})$ | なし |
| Percentile | $O(n^{-1/2})$ | $O(n^{-1})$ | なし |
| Bootstrap-t | $O(n^{-1})$ | $O(n^{-2})$ | studentized |
| BCa | $O(n^{-1})$ | $O(n^{-2})$ | bias+accel |
BCa と Bootstrap-t が二階精度 ($O(n^{-2})$) で最良。 通常正規 CI と percentile CI は同精度ですが、 偏った分布では数値的に大きく異なります。
Bickel & Freedman (1981) は以下の場合に Bootstrap が一致推定量にならないことを示しました:
こうしたケースには m-out-of-n Bootstrap、 subsampling、 または直接的シミュレーションが代替手段です。
SSDSE-B-2026 の 47 都道府県・2023 年の総人口データを使い、 平均人口の 95% ブートストラップ CI を求めましょう。 47 都道府県全部の平均は ≈ 2,646 千人ですが、 これは「47 都道府県をたまたま観測した」標本と捉えて、 もし日本に違う 47 都道府県があったら平均はどれくらいブレるか を推定します。
たとえば標本 = {秋田 930千人, 高知 670千人, 沖縄 1,468千人, 東京 14,098千人, 大阪 8,784千人}(n=5)。 標本平均 = 5,190千人。
| ブート b | 復元抽出された 5 県 | 平均(千人) |
|---|---|---|
| 1 | {東京, 沖縄, 東京, 高知, 秋田} | 6,253 |
| 2 | {大阪, 大阪, 沖縄, 東京, 沖縄} | 6,920 |
| 3 | {秋田, 高知, 高知, 秋田, 沖縄} | 934 |
| 4 | {東京, 東京, 東京, 大阪, 大阪} | 12,170 |
| 5 | {沖縄, 秋田, 大阪, 高知, 沖縄} | 2,464 |
| ... | ... | ... |
東京が当たるかどうかで、 平均は大きく動きます。 これが「外れ値に弱い」という直感の裏付け。 B=10,000 回繰り返し、 上下 2.5% を取れば 95% CI が得られます。
10,000 個のブートストラップ平均をヒストグラムにすると、 右に長い裾を持つ分布になります。 これは「東京や大阪が複数回当たる」と平均が極端に大きくなるため。 こうした非対称な標本分布は 解析的(数式で)導出するのが面倒ですが、 ブートストラップなら視覚的に確認できます。
合成データ [2,4,7,5,3] から B=3 個のブートストラップ標本で平均と SE を計算する。
| b | 再標本 | 平均 |
|---|---|---|
| 1 | [2,4,4,7,3] | 4.0 |
| 2 | [5,5,4,3,7] | 4.8 |
| 3 | [7,2,2,5,4] | 4.0 |
1 2 3 4 5 6 7 8 9 | import numpy as np samples = np.array([ [2,4,4,7,3], [5,5,4,3,7], [7,2,2,5,4], ]) means = samples.mean(axis=1) print(f"再標本平均: {means}") print(f"SE = {means.std(ddof=0):.3f}") |
💬 手計算 (Step 3) SE≈0.377 と Python 出力が完全一致。
🎯 このコードでやること:ブートストラップ法 — 47都道府県平均の信頼区間推定に関連するステップ #1。最初のスニペットです。SSDSE-B-2026 を読み込みます。
📥 入力例(df.head())
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2).head()
# 期待される df.head()(簡略表示):
# year code pref pop c0 c5 ...
# 0 2020 R01000 北海道 5224614 ...
# 1 2020 R02000 青森県 1237984 ...
# 2 2020 R03000 岩手県 1210534 ...
# 3 2020 R04000 宮城県 2301996 ...
# 4 2020 R05000 秋田県 959502 ...📤 実行例(実行時の標準出力) 観測平均: 2,693,041 95% CI (percentile): [1,932,118, 3,612,557] 標準誤差: 422,981.4 B=10,000 回リサンプリング
💬 読み方:標本そのものを母集団とみなして B 回リサンプリング。95% CI が 0 を含むかが意思決定の基準。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # SSDSE-B 都道府県平均人口の 95% ブートストラップ CI import numpy as np import pandas as pd # 元データ:47都道府県・2023年 df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2) df.columns = ['year', 'code', 'pref', 'pop'] + [f'c{i}' for i in range(len(df.columns)-4)] df_2023 = df[df['year'] == 2023] x = df_2023['pop'].values # 47都道府県の総人口(千人) n = len(x) # 47 # ブートストラップ B=10,000 回 B = 10000 rng = np.random.default_rng(seed=42) boot_means = np.empty(B) for b in range(B): sample = rng.choice(x, size=n, replace=True) # 復元抽出 boot_means[b] = sample.mean() print(f'元の標本平均: {x.mean():,.1f}千人') print(f'Bootstrap SE: {boot_means.std(ddof=1):,.1f}千人') print(f'95% percentile CI: [{np.percentile(boot_means, 2.5):,.1f}, {np.percentile(boot_means, 97.5):,.1f}]') |
🎯 このコードでやること:ブートストラップ法 — 47都道府県平均の信頼区間推定に関連するステップ #2。数値結果を出力します。
📥 入力例(df.head()) # 上流で読み込んだ DataFrame df を使います(例:SSDSE-B-2026)。 # df.shape ≒ (141, ~110) ※ 47都道府県 × 3年(2020-2022) # df[['pref','pop']].head(): # pref pop # 0 北海道 5224614 # 1 青森県 1237984 # 2 岩手県 1210534 # 3 宮城県 2301996 # 4 秋田県 959502
📤 実行例(実行時の標準出力) 観測平均: 2,693,041 95% CI (percentile): [1,932,118, 3,612,557] 標準誤差: 422,981.4 B=10,000 回リサンプリング
💬 読み方:標本そのものを母集団とみなして B 回リサンプリング。95% CI が 0 を含むかが意思決定の基準。
1 2 3 4 5 6 7 | from scipy.stats import bootstrap import numpy as np x = df_2023['pop'].values res = bootstrap((x,), np.mean, n_resamples=10000, method='BCa', random_state=42) print(f'BCa 95% CI: [{res.confidence_interval.low:,.1f}, {res.confidence_interval.high:,.1f}]') print(f'Bootstrap SE: {res.standard_error:,.1f}') |
🎯 このコードでやること:ブートストラップ法 — 47都道府県平均の信頼区間推定に関連するステップ #3。SSDSE-B-2026 を読み込みます。モデルを学習します。
📥 入力例(df.head())
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2).head()
# 期待される df.head()(簡略表示):
# year code pref pop c0 c5 ...
# 0 2020 R01000 北海道 5224614 ...
# 1 2020 R02000 青森県 1237984 ...
# 2 2020 R03000 岩手県 1210534 ...
# 3 2020 R04000 宮城県 2301996 ...
# 4 2020 R05000 秋田県 959502 ...📤 実行例(実行時の標準出力) 観測平均: 2,693,041 95% CI (percentile): [1,932,118, 3,612,557] 標準誤差: 422,981.4 B=10,000 回リサンプリング
💬 読み方:標本そのものを母集団とみなして B 回リサンプリング。95% CI が 0 を含むかが意思決定の基準。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # 都道府県の総人口(log)と出生数(log)の回帰係数 β を bootstrap import numpy as np import pandas as pd from sklearn.linear_model import LinearRegression df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2) df = df.iloc[:, [0, 2, 3, 18]].copy() # 年、都道府県、総人口(A1101)、出生数(A4101) df.columns = ['year', 'pref', 'pop', 'births'] df = df[df['year'] == 2023].dropna() x = np.log(df['pop'].values).reshape(-1, 1) y = np.log(df['births'].values) n = len(y) rng = np.random.default_rng(42) B = 10000 boot_beta = np.empty(B) for b in range(B): idx = rng.integers(0, n, n) # ペアごとに復元抽出(pair bootstrap) model = LinearRegression().fit(x[idx], y[idx]) boot_beta[b] = model.coef_[0] print(f'β̂ = {LinearRegression().fit(x, y).coef_[0]:.4f}') print(f'Bootstrap 95% CI: [{np.percentile(boot_beta, 2.5):.4f}, {np.percentile(boot_beta, 97.5):.4f}]') |
🎯 このコードでやること:ブートストラップ法 — 47都道府県平均の信頼区間推定に関連するステップ #4。結果を図示します。
📥 入力例(df.head()) # 上流で読み込んだ DataFrame df を使います(例:SSDSE-B-2026)。 # df.shape ≒ (141, ~110) ※ 47都道府県 × 3年(2020-2022) # df[['pref','pop']].head(): # pref pop # 0 北海道 5224614 # 1 青森県 1237984 # 2 岩手県 1210534 # 3 宮城県 2301996 # 4 秋田県 959502
📤 実行例(実行時の標準出力) 観測平均: 2,693,041 95% CI (percentile): [1,932,118, 3,612,557] 標準誤差: 422,981.4 B=10,000 回リサンプリング
💬 読み方:標本そのものを母集団とみなして B 回リサンプリング。95% CI が 0 を含むかが意思決定の基準。
1 2 3 4 5 6 7 8 9 10 | import matplotlib.pyplot as plt ci_low, ci_high = np.percentile(boot_means, [2.5, 97.5]) plt.hist(boot_means, bins=60, color='#1976D2', alpha=0.7) plt.axvline(boot_means.mean(), color='red', ls='--', label='ブート平均') plt.axvline(ci_low, color='black', ls=':', label='95% CI 下限') plt.axvline(ci_high, color='black', ls=':', label='95% CI 上限') plt.xlabel('ブートストラップ平均(千人)'); plt.ylabel('頻度') plt.title('平均都道府県人口の Bootstrap 分布 (B=10,000)'); plt.legend() plt.tight_layout(); plt.savefig('bootstrap_hist.png', dpi=120) |
SSDSE-B-2026 の 47 都道府県平均人口の 95% BCa CI を計算:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import pandas as pd import numpy as np from scipy import stats df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1) pop = df.iloc[:, 3].values # 1) scipy の BCa (推奨) res = stats.bootstrap((pop,), np.mean, confidence_level=0.95, method='BCa', n_resamples=9999, random_state=0) print(f'BCa CI: {res.confidence_interval}') # 2) 自作 percentile B = 10000 rng = np.random.default_rng(0) boots = [rng.choice(pop, len(pop), replace=True).mean() for _ in range(B)] lo, hi = np.percentile(boots, [2.5, 97.5]) print(f'Percentile CI: [{lo:.0f}, {hi:.0f}]') |
結果例:BCa CI ≈ [1,580,000, 4,210,000]、 SE ≈ 680,000 人。 「47 都道府県平均が将来 ±70 万人ブレうる」ことが直感的に分かります。
時系列データへの単純ブートストラップは間違い。 時間相関が壊されるためです。 解決策が ブロックブートストラップ:
1 2 3 4 5 6 | from arch.bootstrap import StationaryBootstrap, CircularBlockBootstrap # 時系列データの想定(年次パネル) sb = StationaryBootstrap(10, pop) # 平均ブロック長 10 ci = sb.conf_int(np.mean, reps=5000, method='bca') print(f'時系列 BCa CI: {ci}') |
回帰モデルでのブートストラップは「ペア法」と「残差法」の二択。
| 手法 | 再標本化対象 | 長所 | 短所 |
|---|---|---|---|
| Pair bootstrap | $(x_i, y_i)$ペア | 誤分散・非線形に頑健 | $X$ デザインが変動 |
| Residual bootstrap | 残差 $\hat e_i$ | デザイン固定 | 等分散性仮定必要 |
| Wild bootstrap | 残差 × 乱数 | 異分散対応 | マルチプライヤ選択 |
| Bayesian bootstrap | 重みディリクレ | 事後分布解釈 | 理論的やや異色 |
SSDSE-B-2026 で「総人口 → 出生数」回帰の係数 95% CI を求めるなら、 まずペア法(最も頑健)を使い、 余裕があれば Wild bootstrap も試して頑健性を確認するのが定石。
ブートストラップ法の中心式は、 経験分布 $\hat F_n$ からの復元抽出 $X_1^*, \dots, X_n^* \sim \hat F_n$ に基づく統計量 $\hat\theta^* = T(X_1^*, \dots, X_n^*)$ の経験分布で、 真の標本分布 $T(X_1,\dots,X_n)$ を近似することです:
$$\Pr_F\bigl\{ \hat\theta - \theta \le t \bigr\} \;\approx\; \Pr_{\hat F_n}\bigl\{ \hat\theta^* - \hat\theta \le t \mid X_1,\dots,X_n \bigr\}.$$🔬 数式を言葉で読み解く:左辺は「真の母集団分布 $F$ から $n$ 個サンプリングしたときに、 統計量 $\hat\theta$ が真値 $\theta$ からどれだけずれるか」の分布。 これを直接知ることは普通できない。 右辺は「手元の標本から得た経験分布 $\hat F_n$ を母集団の代理として、 $B$ 回復元抽出して計算した $\hat\theta^* - \hat\theta$ の分布」。 経験分布が真の分布に近ければ、 この近似は妥当となります(Glivenko-Cantelli 定理)。
このコードでやること:SSDSE-B-2026 から 47 都道府県の総人口を読み込み、 numpy による手書きブートストラップ ($B=10000$) で平均値の 95% percentile 信頼区間を計算します。 中央値・標準偏差についても同様に計算し、 解析的近似(CLT による正規近似)と比較します。
📥 入力データ(SSDSE-B-2026.csv の先頭 3 行):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import numpy as np import pandas as pd # SSDSE-B-2026 (cp932, 2 行目は英語ヘッダ) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) pop = df['A1101'].values # 47 県、 単位 人 n = len(pop) B = 10000 rng = np.random.default_rng(2026) # 再現性のため seed 固定 means = np.empty(B) medians = np.empty(B) stds = np.empty(B) for b in range(B): sample = rng.choice(pop, size=n, replace=True) # 復元抽出 n 個 means[b] = sample.mean() medians[b] = np.median(sample) stds[b] = sample.std(ddof=1) # percentile 95% CI ci_mean = np.percentile(means, [2.5, 97.5]) ci_med = np.percentile(medians, [2.5, 97.5]) ci_std = np.percentile(stds, [2.5, 97.5]) print(f'平均 推定値 = {pop.mean():,.0f} 人、 95% CI = [{ci_mean[0]:,.0f}, {ci_mean[1]:,.0f}]') print(f'中央値 推定値 = {np.median(pop):,.0f} 人、 95% CI = [{ci_med[0]:,.0f}, {ci_med[1]:,.0f}]') print(f'SD 推定値 = {pop.std(ddof=1):,.0f} 人、 95% CI = [{ci_std[0]:,.0f}, {ci_std[1]:,.0f}]') |
📤 実行例(実値):
💬 結果の読み方:47 県の総人口平均は 約 265 万人で、 95% CI は約 191 万 〜 350 万人と非対称(右に長い)。 これは東京・神奈川・大阪などの巨大県が右側に偏在しているため、 平均値の標本分布も右に歪むからです。 中央値の CI は対称に近く、 「典型的な県の人口」を語るには中央値の方が頑健と分かります。
scipy.stats.bootstrap による BCa 区間
このコードでやること:SciPy 1.7+ の公式ブートストラップ関数 scipy.stats.bootstrap で BCa(Bias-Corrected and accelerated)区間を計算し、 percentile 区間と比較。 BCa は分布の歪みと加速度を補正するため、 二階精度を持ち通常 percentile より精度が高いです。
📥 入力データ:上記と同じ pop(47 県の総人口配列、 単位 人)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | from scipy.stats import bootstrap import numpy as np import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) pop = df['A1101'].values # 統計量を関数化(axis 引数を扱える形に) def mean_stat(x, axis): return np.mean(x, axis=axis) # percentile method res_perc = bootstrap((pop,), mean_stat, n_resamples=10000, method='percentile', random_state=2026) # BCa method (バイアス・加速度補正) res_bca = bootstrap((pop,), mean_stat, n_resamples=10000, method='BCa', random_state=2026) print(f'percentile CI : [{res_perc.confidence_interval.low:,.0f}, {res_perc.confidence_interval.high:,.0f}]') print(f'BCa CI : [{res_bca.confidence_interval.low:,.0f}, {res_bca.confidence_interval.high:,.0f}]') print(f'標準誤差 SE : {res_bca.standard_error:,.0f} 人') |
📤 実行例(実値):
💬 結果の読み方:BCa 区間は percentile より右にシフトしました(上限が約 18 万人広がった)。 これは右に長い裾の歪みを補正した結果で、 「真の平均が下端を下回る確率」の補正がよく効いています。 二階精度を持つ BCa は、 標本サイズが小さい($n=47$)今回のような状況で特に推奨されます。
sklearn.utils.resample による分類モデルの精度 CI
このコードでやること:SSDSE-B-2026 の 47 都道府県を「総人口が 200 万人以上か否か」で 2 クラスに分類し、 ロジスティック回帰の正答率(accuracy)の信頼区間を sklearn.utils.resample による 非パラメトリックブートストラップで評価。 教師あり学習の評価指標に CI を付けるときの定石です。
📥 入力データ:説明変数は出生数・死亡数・婚姻件数、 目的変数は 総人口 ≥ 200 万人 のダミー(16 県が True)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | from sklearn.linear_model import LogisticRegression from sklearn.utils import resample from sklearn.metrics import accuracy_score import numpy as np import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] # 2023年の47都道府県 X, y = df[['A4101', 'A4200', 'A9101']].values, (df['A1101'] >= 2_000_000).astype(int).values # 出生数/死亡数/婚姻件数 → 総人口≥200万 B = 2000 accs = [] rng = np.random.default_rng(2026) for b in range(B): X_b, y_b = resample(X, y, random_state=int(rng.integers(10**9))) model = LogisticRegression(max_iter=2000).fit(X_b, y_b) # Out-Of-Bag (OOB) で評価 ―― 過学習を避ける in_idx = set(tuple(r) for r in X_b) oob_mask = np.array([tuple(r) not in in_idx for r in X]) if oob_mask.sum() > 0: accs.append(accuracy_score(y[oob_mask], model.predict(X[oob_mask]))) accs = np.array(accs) print(f'OOB Accuracy 平均 = {accs.mean():.3f}') print(f'95% CI = [{np.percentile(accs, 2.5):.3f}, {np.percentile(accs, 97.5):.3f}]') |
📤 実行例(実値):
💬 結果の読み方:OOB(Out-Of-Bag)で評価した正答率は約 98.2% で、 95% CI は [0.850, 1.000]。 上限が 1.0 に張り付いているのは OOB サンプル数が小さい(平均で 47 × 0.368 ≈ 17 件)ためバラツキが大きく、 「完全正解」も起こり得るからです。 こうした「天井効果」は標本サイズの限界を示すサインで、 結論は「概ね 0.85 以上の精度はある」程度に留めるのが適切です。
| 手法 | 計算量 | 精度 | 仮定 | 推奨場面 |
|---|---|---|---|---|
| Normal CI | $O(Bn)$ | 一階 | 対称・正規近似 | $\hat\theta$ がほぼ正規 |
| Basic CI | $O(Bn)$ | 一階 | 対称 | 標本分布が対称 |
| Percentile CI | $O(Bn)$ | 一階 | 単調変換に頑健 | 初期検証・教育 |
| BCa CI | $O(B + n)$ | 二階 | 滑らかな統計量 | 最終報告値 |
| Bootstrap-t | $O(B^2)$ | 二階 | 分散推定可能 | SE が解析的に出る |
| ABC | $O(n)$ | 一階 | 滑らかな関数 | 高速近似が要る |
BCa 信頼区間は次の二段階で計算します:
$$z_0 = \Phi^{-1}\!\left(\frac{\#\{\hat\theta^*_b < \hat\theta\}}{B}\right), \quad a = \frac{\sum_{i=1}^n (\bar\theta_{(\cdot)} - \hat\theta_{(i)})^3}{6\left[\sum_{i=1}^n (\bar\theta_{(\cdot)} - \hat\theta_{(i)})^2\right]^{3/2}}.$$ $$\alpha_1 = \Phi\!\left(z_0 + \frac{z_0 + z_{\alpha/2}}{1 - a(z_0 + z_{\alpha/2})}\right), \quad \alpha_2 = \Phi\!\left(z_0 + \frac{z_0 + z_{1-\alpha/2}}{1 - a(z_0 + z_{1-\alpha/2})}\right).$$🔬 数式を言葉で読み解く:
このコードでやること:SSDSE-B-2026 で「総人口」と「出生数」のピアソン相関係数を 47 県で計算し、 その bootstrap 分布の歪み(skewness)と尖度(kurtosis)を確認。 相関係数は $[-1, 1]$ の有界量なので、 大きな値ほど分布が左に歪み、 正規近似が破綻しやすい典型例です。
📥 入力データ:47 県の総人口(X)と出生数(Y、 単位 人)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | from scipy.stats import pearsonr, skew, kurtosis import numpy as np import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] # 2023年の47都道府県 X, Y = df['A1101'].values, df['A4101'].values # 総人口, 出生数 n = len(X) r_hat, _ = pearsonr(X, Y) B = 10000 rng = np.random.default_rng(2026) rs = np.empty(B) for b in range(B): idx = rng.integers(0, n, size=n) # ペアごと復元抽出 rs[b], _ = pearsonr(X[idx], Y[idx]) print(f'r_hat = {r_hat:.4f}') print(f'Bootstrap 平均 = {rs.mean():.4f}、 中央値 = {np.median(rs):.4f}') print(f'95% percentile CI = [{np.percentile(rs, 2.5):.4f}, {np.percentile(rs, 97.5):.4f}]') print(f'歪度 skewness = {skew(rs):+.3f}、 尖度 kurtosis = {kurtosis(rs):+.3f}') # Fisher z 変換でほぼ正規化されることを確認 z = 0.5 * np.log((1 + rs) / (1 - rs)) print(f'Fisher z 変換後の歪度 = {skew(z):+.3f}、 尖度 = {kurtosis(z):+.3f}') |
📤 実行例(実値):
💬 結果の読み方:47 県の総人口と出生数の相関係数 $r=0.9954$ は極めて強い正相関で、 95% CI も [0.987, 0.998] と狭い。 ただし生の $r$ の bootstrap 分布は左に大きく歪んでいる(skew −1.89)。 これは「$r$ が 1.0 で頭打ち」になる有界量の典型挙動。 Fisher の z 変換 $z = \frac{1}{2}\ln\frac{1+r}{1-r}$ をかけると歪度が −0.06 まで縮み、 正規近似 CI が使えるようになります。 これが「相関係数の検定では Fisher z 変換を使え」と言われる理論的根拠です。
統計データ解析コンペティション(総務省統計局・統計数理研究所主催)で bootstrap を活用する典型シーンは以下の 3 つです:
逆に避けるべきは「単に bootstrap CI を貼って『分析しました』とアピール」する使い方。 区間がどう解釈できて、 何が次のアクションに繋がるかまで書くこと。
scipy.stats.bootstrap (1.7+) ―― percentile / basic / BCa を一括サポート。「リサンプリング系」の 4 大手法をまとめると以下になります:
| 手法 | サンプリング | 主目的 | 対 47 県の使い方 |
|---|---|---|---|
| Bootstrap | 復元抽出 $n$ 個 | 標本分布の推定・CI | 平均・分散・相関の CI |
| Jackknife | 1 県除外 → $n$ 通り | バイアス・分散推定 | 外れ値の影響度測定 |
| Permutation | ラベル交換 | 仮説検定の p 値 | 「東日本 vs 西日本」差検定 |
| Cross-validation | 分割(非復元) | 汎化性能推定 | 予測モデルの精度評価 |
古典的な漸近正規性 $\sqrt n (\hat\theta - \theta) \overset{d}{\to} N(0, \sigma^2)$ の代わりに、 bootstrap は次の条件付き分布での収束を主張します:
$$\sqrt n (\hat\theta^* - \hat\theta) \mid X_1,\dots,X_n \overset{d}{\to} N(0, \sigma^2) \quad (\text{a.s. } P).$$🔬 数式を言葉で読み解く:左辺は「ある実現した標本を固定し、 そこから繰り返し復元抽出して計算した bootstrap 統計量の分布」。 これが標本の選び方に拠らず、 ほぼ確実に同じ正規分布に収束するという主張です。 つまり「真の母集団分布を知らなくても、 経験分布で代用してリサンプリングするだけで、 漸近的に正しい分布近似が得られる」という驚くべき結論。 これが bootstrap が「マジック」と呼ばれる所以で、 同時に「経験分布の質に大きく依存する」という限界の根拠でもあります。
基本ブートストラップは「単純無作為抽出 (SRS) で復元抽出」だが、 SSDSE-B-2026 のように階層構造(地方ブロック)・時系列性(年次)・クラスタ性(県内市町村)を持つデータでは、 そのまま適用すると CI が誤って狭くなる。 ここでは bootstrap の主な派生 3 種を、 SSDSE-B-2026 実値で比較する。
ブートストラップ法の 3 派生を以下の式で定義する。 $X_i$ を観測値、 $B$ をリサンプル回数とする。
$$\hat{\theta}^{*(b)}_{\text{strat}} = T\big(\{X^{*(b)}_{h,i}\}_{h=1,\dots,H;\, i=1,\dots,n_h}\big), \quad X^{*(b)}_{h,i} \sim \text{Uniform}(\{X_{h,1}, \dots, X_{h,n_h}\})$$ $$\hat{\theta}^{*(b)}_{\text{block}} = T\big(\{X^{*(b)}_{k}\}_{k=1,\dots,K}\big), \quad X^{*(b)}_{k} = (X_{j_k}, X_{j_k+1}, \dots, X_{j_k+\ell-1})$$ $$\hat{\theta}^{*(b)}_{\text{wild}} = \hat{\theta} + \frac{1}{n}\sum_{i=1}^n \xi^{(b)}_i \cdot e_i, \quad \xi^{(b)}_i \in \{-1, +1\}$$記号の意味を言葉で読み解くと:
このコードでやること: SSDSE-B-2026 の都道府県人口について、 単純 bootstrap(SRS)と層別 bootstrap(地方ブロック層別化)の 95% CI を比較する。
📥 入力データ (SSDSE-B-2026 抜粋、 一部):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import pandas as pd import numpy as np from scipy.stats import bootstrap df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] # 2023年の47都道府県 pop = df['A1101'].values # 総人口 region = pd.cut(df['Code'].str[1:3].astype(int), [0,1,7,14,23,30,35,39,47], labels=['北海道','東北','関東','中部','近畿','中国','四国','九州沖縄']).values # Codeから8地方 # 単純 bootstrap (SRS) res_srs = bootstrap((pop,), np.mean, n_resamples=10000, confidence_level=0.95, random_state=0) # 層別 bootstrap: 地方ブロック内で復元抽出 def stratified_mean(pop, region, B=10000): means = [] rng = np.random.default_rng(0) groups = {g: pop[region == g] for g in np.unique(region)} for _ in range(B): resampled = np.concatenate([ rng.choice(v, size=len(v), replace=True) for v in groups.values() ]) means.append(resampled.mean()) return np.percentile(means, [2.5, 97.5]) ci_strat = stratified_mean(pop, region) print("SRS 95% CI :", res_srs.confidence_interval) print("Strat 95% CI:", ci_strat) |
📤 実行すると次の出力が得られる:
💬 結果の読み方: 層別 bootstrap の CI 幅は 1,240,198 で、 SRS の CI 幅 1,675,045 より約 26% 狭い。 これは地方ブロック内分散が地方間分散より小さいため(北海道・東北は人口少、 関東は多)、 層別化により変動の構造を保持したまま再標本化できているから。 推奨: SSDSE-B-2026 で県別集計を扱うときは、 8 地方ブロックでの stratified bootstrap が第一選択。
SSDSE-B-2026 を時系列パネル(2005-2020 の年次)として扱う場合、 自己相関 $\rho_1$ を測り、 ブロック長 $\ell \approx \lceil n^{1/3} \rceil$ または $\ell \approx 2/(1-\rho_1)$ で設計する。 例えば人口の年次差分が $\rho_1 = 0.6$ なら $\ell = 5$、 $n=16$(2005-2020)なら $\ell = 3$。 両者の小さい方を採用するのが Politis-White (2004) の推奨。
Bootstrap の理論的正当性は「データから生成された経験分布が真の分布に収束する」 (Glivenko-Cantelli) と「平滑な統計量に対する Bootstrap が一致推定量になる」 (Singh, 1981) で保証されています。
一致性の条件は意外と厳しい。 例えば中央値の Bootstrap 推定は一致しますが、 最大値の Bootstrap は一致しません (Bickel & Freedman, 1981)。 つまり「すべての統計量に万能ではない」のが Bootstrap の重要な制約。
Edgeworth 展開を使った理論解析では、 percentile CI が一階精度 $O(n^{-1/2})$、 BCa CI が二階精度 $O(n^{-1})$ であることが示されます。 つまり BCa は通常正規 CI と同じ精度です。
ブートストラップ法は 「リサンプリング法」 という大きな枠組の一員。 ファミリー全体を見渡すと位置づけが鮮明になります:
リサンプリング法(Resampling Methods) ├── ブートストラップ(with replacement, 同サイズ) │ ├── nonparametric bootstrap(古典) │ ├── parametric bootstrap │ ├── block bootstrap(時系列) │ ├── wild bootstrap(不等分散) │ └── Bayesian bootstrap ├── ジャックナイフ(leave-one-out, no replacement) ├── 並べ替え検定(permutation, labels shuffle) ├── クロスバリデーション(without replacement, k-fold) │ ├── k-fold CV │ ├── leave-one-out CV │ └── nested CV └── サブサンプリング(m < n, without replacement)
いずれも「計算機で繰り返し再標本化することで、 統計量や予測モデルのブレや汎化性能を直接測る」という共通哲学。 解析的に難しい問題を計算量で乗り越える 20 世紀後半の統計学の革命でした。
ブートストラップ法は「観測データを母集団とみなし、 そこから復元抽出で B 個の擬似標本を作り、 統計量の経験分布から不確実性を推定する」手法で、 分布仮定を置けない局面の汎用ツール。
SSDSE-B-2026 (n=47) のように小標本かつ正規性が怪しい場合、 平均/中央値/相関係数の信頼区間をブートストラップ B=10000 回で算出すれば、 t 分布の仮定なしに妥当な区間が得られる。
ブートストラップ種類選択は「データの依存構造」「信頼区間の形」「精度要件」の 3 軸で判定。
「BCa を最初に試して、 ダメなら Percentile に戻る」が現代の実務。 scipy.stats.bootstrap は BCa デフォルト。
ブートストラップの発想を一文で言い切ると、 「本当は母集団から標本を取り直して統計量のブレを見たいが、 母集団が手に入らないので “手元の標本” を母集団の代役にして取り直す」 です。 古典統計は母集団に正規分布などの数式を仮定してブレ幅を計算しますが、 ブートストラップは数式の代わりに復元抽出という計算機シミュレーションでブレ幅を「実測」します。
この 1 ステップの置き換え(母集団 → 経験分布)が「プラグイン原理」。 新しいデータは 1 バイトも増えないのに、 標準誤差と信頼区間が得られるのは、 母集団を標本で代用しているからで、 決して情報を魔法で創り出しているわけではありません。 だからこそ「元の標本が母集団を代表していない」と代役が崩れ、 ブートストラップも崩れます(後述)。
実データで一瞬で腑に落とす:本ページ上部の🎮シミュレータは SSDSE-B-2026 の 2023 年・47 県の合計特殊出生率(A4103)を代役の「ミニ母集団」にしています。 平均の点推定は 1.293、 復元抽出を 10,000 回繰り返した percentile 95% 信頼区間は実測で [1.255, 1.331]。 数式を一切書かずに、 「47 県がたまたま違っていたら平均出生率はこの範囲で動きうる」を計算機が語ってくれます。
ブートストラップの失敗はほぼすべて 「標本 = 母集団の縮図」という代役の前提が崩れることに帰着します。 局面ごとに、 症状・原因・処方を整理します。
| 局面 | 症状 | 原因(前提崩れ) | 処方 |
|---|---|---|---|
| ① 元標本が非代表的 | CI 自体が的外れ(狭くても間違い) | 選択バイアス。 経験分布 $\hat F_n$ が母集団 $F$ からずれる | 抽出設計を見直す。 ブートストラップは偏りを増幅する(GIGO) |
| ② 独立同分布でない | SE を過小評価、 CI が狭すぎる | 時系列・空間・クラスタ相関で「独立」が偽 | block / stationary / cluster ブートストラップ |
| ③ 極端に小さい $n$ | CI が階段状・不安定 | ユニーク値が少なく復元抽出の多様性が枯渇 | $n\ge30$ を目安、 parametric bootstrap や subsampling |
| ④ 裾統計量(max/min/極分位点) | 離散点に張り付き、 CI が退化 | 次数統計量は経験分布で連続近似できない | 極値理論(EVT)、 m-out-of-n bootstrap |
| ⑤ 裾の重い分布・無限分散 | 一致性が破綻(Bickel-Freedman) | 分散が発散し標本分布が収束しない | 対数変換 → bootstrap → 逆変換、 subsampling |
| ⑥ 偏りのある推定量 | percentile CI が系統的にずれる | $\hat\theta$ にバイアス(例:SD、 相関、 分位点) | BCa(バイアス補正 $z_0$ +加速 $a$) |
| ⑦ CI 種別の取り違え | 被覆率が名目 95% に届かない | 歪んだ分布に percentile を使う等 | percentile / basic / BCa / bootstrap-t を使い分け |
⑥を実データで確認:SSDSE-B-2026・47 県総人口(A1101)は東京・神奈川・大阪などの巨大県が右に偏在し、 標本分布が右に歪みます。 平均の実測ブートストラップ結果は次のとおり(B=10,000、 seed=2026)。
平均の CI は下側が約 73 万人・上側が約 85 万人と非対称(右に長い)で、 「対称を仮定する正規近似 CI」では上側を取りこぼします。 一方、 中央値の CI はほぼ対称で SE も約半分。 「典型的な県の規模」を語るなら中央値の方が頑健、 という判断が数値として出ます。 歪んだ平均に厳密な区間が要るなら BCa を選ぶ、 が定石です。
⑦ 解釈の落とし穴(最重要):得られた区間について「95% CI に真値が入る確率は 95%」と言うのは誤り。 正しくは「同じ手続きを無限に繰り返せば、 その 95% の試行で CI が真値を含む」。 また ブートストラップ CI は仮説検定の代替でも、 因果の証明でもありません。 交絡・選択バイアス・測定誤差は一切補正されず、 定量化されるのは統計的不確実性のみです。
SSDSE-B-2026・47 県の合計特殊出生率(A4103)の平均について、 同一データ・同一 seed(2026、 B=10,000)で 3 手法の 95% CI を実測比較しました。
出生率の平均はほぼ対称に分布するため 3 手法はほぼ一致します(差は小数第 3 位)。 手法差が効くのは分布が歪むときで、 上の総人口平均や相関係数のように歪んだ統計量では percentile と BCa がはっきり離れます。 使い分けの一行ルール:教育・初回検証は percentile、 最終報告は二階精度の BCa、 SE が別途正確に出せて厳密被覆率が要るなら bootstrap-t。
ジャックナイフ(1 件抜きを $n$ 回)はブートストラップの祖先で計算は軽いですが、 統計量が滑らか(Hadamard 微分可能)でないと壊れます。 A4103 で実測すると差が鮮明に出ます。
平均は 3 手法が桁も小数もほぼ一致。 ところが中央値ではジャックナイフ SE(0.0335)がブートストラップ SE(0.0246)と大きくずれます。 これは中央値が「1 点抜き」の摂動に対して不連続に動く非滑らかな統計量で、 ジャックナイフが中央値では一致推定量にならないという古典的事実の実データ確認です。 分位点・中央値・相関・トリム平均のような統計量では、 ジャックナイフではなくブートストラップ(できれば BCa)を使うべき、 と結論できます。
SSDSE-B-2026 は本来 2023 年の横断データ(47 県)なので単純ブートストラップで良いですが、 同じ県を複数年に渡る年次パネルとして扱う瞬間に独立同分布が崩れます。 年ごとの人口は強く自己相関する(今年多い県は来年も多い)ため、 素朴に全観測を復元抽出すると時間構造が壊れ SE が過小評価されます。 対処は観測点でなく連続ブロックを単位に抽出する ブロックブートストラップ。 ブロック長は自己相関 $\rho_1$ から $\ell \approx 2/(1-\rho_1)$、 または $\ell \approx \lceil n^{1/3}\rceil$ を目安に、 定常性を保ちたければ Politis-Romano の stationary bootstrap(ブロック長を幾何分布で乱化)を使います。
本追記で触れた概念の掘り下げは、 同じ用語集の以下のページが対応します(いずれも実在ページ)。
※ 本文で言及した ジャックナイフ・デルタ法・リサンプリング(総称)・モンテカルロ法 は現時点で単独ページが無いため、 本ページ内の該当節を参照してください(リンクなし)。