95% CI = [a, b] を、数式不要で求める。論文の表や 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 法 の基本形です。
元の標本を $\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 行。💡 重要:ブートストラップは「母集団から繰り返し標本を取る」のではなく「標本から繰り返し復元抽出する」操作です。 つまり「標本 = 母集団の縮図」と信じるのがキモ。 この仮定が破れるとブートストラップも破綻する(後述の落とし穴)。
SSDSE-B-2026 の 47 都道府県・2023 年の総人口データを使い、 平均人口の 95% ブートストラップ CI を求めましょう。 47 都道府県全部の平均は ≈ 2,728 千人ですが、 これは「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 個のブートストラップ平均をヒストグラムにすると、 右に長い裾を持つ分布になります。 これは「東京や大阪が複数回当たる」と平均が極端に大きくなるため。 こうした非対称な標本分布は 解析的(数式で)導出するのが面倒ですが、 ブートストラップなら視覚的に確認できます。
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}]') |
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}') |
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, 31]].copy() # 年、都道府県、人口、県内総生産 df.columns = ['year', 'pref', 'pop', 'gdp'] df = df[df['year'] == 2023].dropna() x = np.log(df['pop'].values).reshape(-1, 1) y = np.log(df['gdp'].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}]') |
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) |
ブートストラップは Efron の原論文(1979)以来、 数十の派生バージョンが提案されてきました。 主要なものを整理します:
| 派生手法 | 状況 | 骨子 |
|---|---|---|
| parametric bootstrap | 分布の仮定がある場合 | 経験分布の代わりにパラメータ推定済みの分布から抽出。 例:$\hat{\mu}, \hat{\sigma}^2$ で正規分布から再標本化 |
| block bootstrap | 時系列・空間データ | 長さ $k$ の連続ブロックを単位として復元抽出。 自己相関を保つ |
| residual bootstrap | 回帰モデル | $\hat{y}_i$ を固定し、 残差 $\hat{\varepsilon}_i$ を復元抽出して新しい $y_i^* = \hat{y}_i + \hat{\varepsilon}^*_i$ を作る |
| wild bootstrap | 不等分散の回帰 | 残差を ±1 のランダム符号で乗算。 heteroskedasticity-robust |
| BCa bootstrap | 分布が歪む場合 | パーセンタイル CI にバイアス補正と加速度を加える。 sciPy のデフォルトの 1 つ |
| bootstrap-t (studentized) | 正確な被覆率が必要 | 各複製で SE も計算し、 t 統計量の分布を bootstrap。 計算コストは高いが被覆率は最良 |
| m-out-of-n bootstrap | 分位点・末尾統計量 | サイズ m < n のサブサンプル。 standard bootstrap が破綻する状況で使用 |
| Bayesian bootstrap | ベイズ的解釈 | 復元抽出ではなく、 ディリクレ分布から重みを引いて加重平均。 事後分布の近似に使える |
| jackknife | ブートの祖先 | n 個から 1 個ずつ抜いた n 個の標本で統計量を計算。 SE の linearization 近似 |
| permutation test | 仮説検定の親戚 | 群ラベルをシャッフルして H₀ 下の分布を作る。 同じ「再標本化」族 |
使い分けの一行ルール:データが iid で分布の仮定を避けたい → standard nonparametric bootstrap。 時系列 → block。 分布の歪みが強い → BCa。 厳密な CI 被覆率が必要 → bootstrap-t。
ブートストラップ法は 「リサンプリング法」 という大きな枠組の一員。 ファミリー全体を見渡すと位置づけが鮮明になります:
リサンプリング法(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 世紀後半の統計学の革命でした。
「ブートストラップ CI が広すぎる」「BCa と percentile で結果が違う」「収束しない」など困ったら、 (1) B をもっと大きく、 (2) BCa を試す、 (3) サンプルサイズと統計量の組合せが極端統計量でないか確認、 (4) 観測の独立性を再点検。 Cross Validated(Stack Exchange)で類例を探すのも有効。
ブートストラップは Efron (1979) の発明。 「手元データの経験分布 $\hat F_n$ を真の分布 $F$ の代理にする」というシンプルな原理で、 解析的には困難な統計量の標準誤差・信頼区間を計算機シミュレーションで導きます。
$$X^{*} \sim \hat F_n \quad \Leftrightarrow \quad X^{*} = X_I, \quad I \sim \text{Unif}\{1,\ldots,n\}$$
SSDSE-B-2026 の 47 都道府県平均人口の SE を求めたいとき、 通常は中心極限定理で $\hat\sigma/\sqrt{n}$ を計算しますが、 ブートストラップなら「中央値の SE」「相関係数の CI」のような 解析公式が存在しない統計量でも一貫した方法で扱えます。
| 手法 | 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 をデフォルトに採用しています。
SSDSE-B-2026 の 47 都道府県平均人口の 95% BCa CI を計算:
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 万人ブレうる」ことが直感的に分かります。
時系列データへの単純ブートストラップは間違い。 時間相関が壊されるためです。 解決策が ブロックブートストラップ:
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 で「人口 → GDP」回帰の係数 95% CI を求めるなら、 まずペア法(最も頑健)を使い、 余裕があれば Wild bootstrap も試して頑健性を確認するのが定石。
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 と同じ精度です。
Bootstrap の親戚たち:
実務で Bootstrap が役立つシーン:
Q. $B$ 回はいくつあれば?
A. SE 推定なら $B=200$、 90% CI なら $B=1000$、 95% CI なら $B=5000$、 99% CI なら $B=10000$ が経験則。 BCa はさらに $n$ 倍重い jackknife を必要とします。
Q. Bootstrap と CLT の使い分けは?
A. CLT は「標本平均が正規」と言うだけ。 中央値・分位点・相関・複合指標などは CLT が直接適用できないので Bootstrap。 また小標本では CLT の収束が遅く Bootstrap の方が信頼できることも。
Q. 時系列に普通の Bootstrap は使える?
A. 使えません。 時間相関を壊すので。 Block Bootstrap、 Stationary Bootstrap、 または AR モデルの残差 Bootstrap を使います。
Q. Bootstrap が「失敗」する場合は?
A. 極値統計量(最大・最小)、 「データ全体の関数」、 重い裾を持つ分布、 強い従属性、 小標本($n < 10$)など。 こうした場合は m-out-of-n Bootstrap や subsampling が代替。
Q. BCa CI と percentile CI、 どっち?
A. 理論的・実用的に BCa が優位。 percentile は教育用・対称分布の単純なケース。 scipy.stats.bootstrap のデフォルトが BCa です。
Q. Bayesian Bootstrap との違いは?
A. 通常 Bootstrap は離散的に重複度を変える(多項分布)。 Bayesian Bootstrap は連続的にディリクレ重みを与える。 結果は漸近的に等価だが、 事後分布解釈が明示的。
Q. ペア法 vs 残差法の使い分けは?
A. 異分散・誤特定が疑われるなら必ずペア法。 デザイン行列を保ちたい・等分散仮定が妥当なら残差法。 迷ったらペア法(より頑健)。
Q. 並列化のコツは?
A. `joblib.Parallel(n_jobs=-1)(delayed(boot_func)(rng) for _ in range(B))`。 シードを各ジョブに分けて再現性確保。
臨床試験
治療効果の SE と CI、 ARR (Absolute Risk Reduction) の不確実性。
ファイナンス
VaR、 シャープレシオ、 トラッキングエラーの CI。
機械学習
Bagging (Random Forest)、 予測 CI、 特徴重要度の SE。
計量経済学
弾力性・処置効果の SE、 GMM 推定量の不確実性。
品質管理
CpK、 Process Capability の CI。
A/B テスト
効果サイズの Bootstrap CI、 ベイズ Bootstrap で事後分布。
生物統計
マイクロアレイの遺伝子発現比較、 補正後 p 値。
スポーツ分析
プレーヤー指標の CI、 期待値計算。
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、 または直接的シミュレーションが代替手段です。