Let's say we want to calculate the two-sided $p$-value for a linear regression coefficient using bootstrapping. The null hypothesis is $\beta_1 = 0$. For the non-parametric (case-resampling) bootstrap, we would do the following steps:
- Calculate the original statistic $T_0$, e.g. the $t$-statistic of the coefficient of interest in the original sample.
- Sample the observations with replacement to create a bootstrap sample of the same size as the original sample.
- Calculate the regression on this bootstrap sample and store the bootstrap statistic $T^*$, i.e. the coefficient of interest.
- Repeat steps 2 and 3 a large number of times, say $R$ (e.g. $R = 1000$). This gives us $R$ bootstrapped statistics $T_r^*$.
- Shift the distribution of $T_r^*$ to generate the null distribution.
- Calculate the two-sided $p$-value by calculating: $p_{\text{boot}} = \dfrac{\text{#}\{|T_r^*| \geq |T_0|\} + 1}{R + 1}$.
My question concerns step 5: Should we shift the bootstrapped statistics by $T_r^*-\bar{T}_r^*$ so that its distribution is centered around $0$ or by $T_r^*-T_0$ so that it's centered around at the bias $\hat{B}^*=\bar{T}_r^* - T_0$?
The first version respects the bootstrap analogy that the sample is our population regarding the bootstrap samples. The second version makes sense because it corresponds exactly to the null value specified in the null hypothesis.
Here is a small R script that illustrates the procedures. The $p$-values calculated by the two versions are virtually identical here (the bias is only $0.12$) but I suspect that this is not always the case, especially when the bias is large.
# Load data
data(swiss)
Calculate original model
mod <- lm(Infant.Mortality~., data = swiss)
Store sample size
N <- nobs(mod)
Store original t-statistic for "Fertility"
stat_orig <- summary(mod)$coefficients["Fertility", "t value"]
Start the bootstrap
R <- 10000 # Number of bootstrap samples
Start resampling
set.seed(142857) # Reproducibility
stat_boot <- replicate(R, {
boot_ind <- sample.int(N, replace = TRUE) # Sample indices with replacement
tmp_mod <- lm(Infant.Mortality~., data = swiss[boot_ind, ])
summary(tmp_mod)$coefficients["Fertility", "t value"]
})
Create null distribution by shifting bootstrap distribution
stat_boot_1 <- stat_boot - mean(stat_boot) # Mean centered
stat_boot_2 <- stat_boot - stat_orig # Centered on the bias
Calculate p-values
(pval_boot_1 <- (sum(abs(stat_boot_1) >= abs(stat_orig)) + 1)/(R + 1))
[1] 0.03019698
(pval_boot_1 <- (sum(abs(stat_boot_2) >= abs(stat_orig)) + 1)/(R + 1))
[1] 0.03149685