Finite-sample coverage of CR0, CR1 and wild-cluster-bootstrap-t intervals for a mean (T2-simulation)
Question
When N observations come from a small number G of unequal-size clusters, how close to 95% is the actual coverage of three cluster-robust intervals for the population mean μ, using the sample mean ȳ as estimator? For each of nine cells (G ∈ {5, 10, 30} × ICC ∈ {0.0, 0.2, 0.5}) we estimate coverage (coverage), its Monte Carlo SE (mc_se_coverage), the relative bias E[V̂]/Var(ȳ) of the CR0 and CR1 variance estimators (rel_bias_var), and the failure regimes where coverage is below 0.90 (failure_cells).
Data and exclusions
There is no observed data. The only input is data/design.csv (9 rows, n_input_rows; SHA-256 f4ff004e…c50, see out/state/data_audit.md). Every row passed the consistency checks (number of sizes = G, sum of sizes = N). No exclusions were applied (out/exclusions.csv: 9 → 9 cells). In each cell R = 2000 replications (R) and B = 399 bootstrap draws (B) were used, giving 18,000 simulated samples (n_analysed). Cell seeds are 20261101–20261109; C1's seed is 20261101 (random_seed, all seeds under seeds). Cluster sizes follow 8, 16, 32, 64 repeated, so with G = 5 the 64-observation cluster contains half the sample. One design-related issue is recorded in data_issues: with G = 5, Rademacher weights can produce only 32 distinct weight vectors. No bootstrap draw had a zero variance (cell_diagnostics.*.n_degenerate_boot = 0).
Method
Each replication draws y_ig = μ + u_g + e_ig, with u_g ~ N(0, ICC) and e_ig ~ N(0, 1−ICC), using numpy.random.default_rng(cell seed). Within each replication the draws are made in this order: u, e, then the B×G Rademacher matrix. The exact variance is Var(ȳ) = [ICC·Σn_g² + (1−ICC)N]/N².
- CR0: V̂ = Σ_g S_g²/N², where S_g is the sum of residuals y − ȳ in cluster g; interval ȳ ± 1.96√V̂.
- CR1: V̂_CR1 = G/(G−1)·V̂_CR0; interval ȳ ± t_{0.975,G−1}√V̂_CR1.
- WCB: unrestricted wild cluster bootstrap-t with Rademacher weights. This is vectorised: ȳ* − ȳ = Σ w_g S_g/N, and the bootstrap cluster residual sums are w_g S_g − n_g(ȳ* − ȳ). These give V̂*_CR1 and |t*_b|. The critical value q is the 0.95 empirical quantile of the 399 |t*| values. I took this to be the
inverted_cdfquantile, which is the 380th order statistic (wcb_quantile_rule). The interval is ȳ ± q√V̂_CR1. A brute-force check that builds y* explicitly matched the vectorised version to 1e-12 (out/state/checks.md). - Coverage is the share of replications with |ȳ − μ| ≤ half-width. MC SE = √(p̂(1−p̂)/R). Relative bias is the mean of V̂ over replications divided by the exact Var(ȳ).
- Baseline (P2): a naive i.i.d.-SE z-interval computed on the same replications (
baseline_iid_coverage). - Running
out/analysis.pyregeneratesresults.json,table1.csvandfigure1.pngin about 2 s. A clean re-run gave byte-identical output.
Results
Full numbers are in out/table1.csv and out/figure1.png.
- Naive baseline: ignoring clustering gives coverage of about 0.29–0.49 whenever ICC > 0 (e.g.
baseline_iid_coverage.iid_G30_icc0.5= 0.2985). At ICC = 0 it gives about 0.94–0.96. Some cluster correction is therefore necessary. - CR0 under-covers in every cell:
- G = 5: 0.7805 / 0.7210 / 0.7170 for ICC 0 / 0.2 / 0.5 (
coverage.cr0_G5_icc0.0,cr0_G5_icc0.2,cr0_G5_icc0.5); - G = 10: 0.8965 / 0.8570 / 0.8530 (
coverage.cr0_G10_*); - G = 30: still only 0.9275 / 0.9210 / 0.9165 (
coverage.cr0_G30_*).
- G = 5: 0.7805 / 0.7210 / 0.7170 for ICC 0 / 0.2 / 0.5 (
- CR0 variance bias: E[V̂_CR0]/Var(ȳ) is 0.66 / 0.53 / 0.48 at G = 5 (
rel_bias_var.cr0_G5_*), 0.86 / 0.76 / 0.77 at G = 10 and 0.94 / 0.93 / 0.93 at G = 30. These Monte Carlo values agree with the analytic expectation (rel_bias_var_exact) within 2.5 MC SE in all 18 cases. - CR1 removes only part of the variance bias: 0.83 / 0.66 / 0.60 at G = 5 (
rel_bias_var.cr1_G5_*) and 0.96–0.98 at G = 30. Its t critical value adds more width.- Coverage at G = 5: 0.9115 / 0.8845 / 0.8755 (
coverage.cr1_G5_*). - Coverage at G = 10: 0.9435 / 0.9155 / 0.9065.
- Coverage at G = 30: 0.9400 / 0.9350 / 0.9300.
- Coverage at G = 5: 0.9115 / 0.8845 / 0.8755 (
- WCB has the best coverage at G = 10 and G = 30 when ICC > 0 (0.9325 and 0.9350 at G = 10,
coverage.wcb_G10_icc0.2,wcb_G10_icc0.5; 0.9445–0.9470 at G = 30). At G = 5 it fails: 0.8575 / 0.8545 / 0.8510 (coverage.wcb_G5_*). This happens even though its mean half-width there is the largest of the three procedures (table1.csv,mean_ci_halfwidth). - Failure regimes (coverage < 0.90): 11 of the 27 pairs (
n_failure_cells,failure_cells):- all three CR0 cells at G = 5 and all three at G = 10;
- all three WCB cells at G = 5;
- CR1 at G = 5 with ICC 0.2 and 0.5.
- Best procedure at G = 5: CR1 has the highest average coverage over the G = 5 cells (0.8905 vs WCB 0.8543 and CR0 0.7395;
best_procedure_small_G,mean_coverage_G5). - Consistent with nominal 95% (|p̂ − 0.95| ≤ 2 MC SE): only 5 pairs (
nominal_cells): CR1 at G = 10 and G = 30 with ICC = 0, and WCB at G = 30 for all three ICC values.
Uncertainty
Each coverage estimate is a binomial proportion based on R = 2000 replications. Its MC SE (mc_se_coverage) runs from 0.0050 (near 0.95) to 0.0101 (near 0.72). A ±2 SE band near the nominal level is therefore about ±0.010–0.011, and differences between procedures smaller than about 0.015 within a cell should not be over-interpreted. However, all procedures in a cell share the same replications, so paired comparisons are more precise than independent SEs suggest. That pairing was not separately quantified.
The MC SE of the relative-bias estimates (rel_bias_var_mcse) is about 0.012–0.014 at G = 5–10 and 0.007–0.009 at G = 30. The largest MC-vs-analytic discrepancy is 2.46 SE (out/state/checks.md).
The "nominal" classification uses a 2-SE rule applied separately to 27 pairs, with no multiplicity adjustment. Pairs close to the boundary (e.g. WCB at G = 10, 0.9325–0.9375) could change class under another seed. Similarly, failure classifications near 0.90 (CR0 at G = 10/ICC 0, 0.8965; CR1 at G = 10/ICC 0.5, 0.9065) are within one MC SE of the threshold.
The WCB critical value has its own Monte Carlo noise from B = 399. That noise is included in the coverage estimate but not separated from it.
Limitations and open issues
- Distributions: only Gaussian random effects and errors were simulated. Skewed or heavy-tailed u_g or e_ig, which are known to hurt few-cluster inference, are not covered.
- Heterogeneity: the ICC is common to all clusters, and errors are homoskedastic within and across clusters. Heterogeneity tied to cluster size, and informative cluster sizes, are not covered.
- Cluster-size design: only one unbalanced pattern (8, 16, 32, 64) was used. With G = 5 a single cluster holds 50% of N, which drives the CR0/CR1 downward bias (high leverage). Balanced designs, or other degrees of imbalance, could give different rankings.
- Regressors: the estimand is a plain mean (intercept-only model). Regression coefficients, especially for treatments that vary only at the cluster level or are given to few clusters, are not covered.
- Other weights and variants:
- Only Rademacher weights were used, in the unrestricted (WCU) form. Webb six-point weights, Mammen weights, restricted (null-imposed, WCR) bootstrap and full enumeration of the 2^5 weight vectors at G = 5 were not studied.
- With G = 5, Rademacher weights give only 16 distinct |t*| values (
data_issues). Under-coverage of WCB at G = 5 is therefore at least partly a property of this weight choice. - CR2/CR3 (Bell–McCaffrey) corrections and other degrees-of-freedom rules were not studied.
- Quantile convention: the WCB quantile rule (380th of 399 order statistics) is my choice for the unspecified "empirical quantile". Other interpolation rules would change q slightly.
- Grid: only G ∈ {5, 10, 30} and three ICC values were simulated. The G at which each procedure reaches nominal coverage is not pinned down.