Create bootstrap resampling indices
resample_bootstrap.RdCreate an index-only resampling plan for bootstrap resampling with replacement.
Each split stores integer row positions from data for model fitting and assessment.
The function supports stratified bootstrap samples and grouped bootstrap samples.
Arguments
- data
(data.frame)
Data set to resample. It must have at least one column and at least two rows.- formula
(formula)
Two-sided model formula. The left-hand side must be a single untransformed column name indata. Calls such ascbind(y1, y2),Surv(time, status),log(y), andfactor(y)are rejected. The right-hand side is ignored when the resampling indices are created.- n_boot
(numeric scalar:
1000L; whole number in[1, .Machine$integer.max])
Number of bootstrap replicates. Whole-number numeric values such as10and10.0are accepted and stored as integers.- strata
(formula:
NULL)
Stratification specification. UseNULLfor no stratification. Use~ 1to stratify by the model outcome. Use a one-sided formula such as~ a + bto stratify by the interaction of columnsaandb.- group
(formula:
NULL)
Grouping specification. UseNULLfor row-level resampling. Use a one-sided formula such as~ subjectto draw whole groups with replacement. The right-hand side must be a single bare column name. Create composite group keys indatabefore callingresample_bootstrap().
Value
An S3 list of class c("bootstrap_resamples", "resamples") with elements:
call: the matched call.method:"bootstrap".n:nrow(data).formula: the inputformula.outcome_var: the resolved outcome column name.strata: the inputstrataformula orNULL.strata_vars: the resolved strata column names orNULL.group: the inputgroupformula orNULL.group_vars: the resolved group column name orNULL.n_groups: the number of unique groups whengroupis supplied, otherwiseNULL.n_boot: the resolved bootstrap count.splits: a named list of split entries. Each split containsanalysis,assessment,oob, andseed.
For ungrouped bootstrap resampling, length(analysis) equals n.
For grouped bootstrap resampling, length(analysis) can differ from n when group sizes differ.
Details
resample_bootstrap() does not copy data.
It returns row indices that can be used to subset data later.
The predictor side of formula is not used to build the splits.
It is accepted so the same formula can be passed to a model-fitting function.
Splits
Each element of splits contains:
analysis: integer row indices drawn with replacement and used to fit a model. Duplicate row indices are expected. WhengroupisNULL, length equalsnrow(data). Whengroupis supplied, length equals the total row count ofn_groupsdrawn groups, which can vary across bootstrap splits if group sizes differ.assessment: integer row indices for the full original data set,seq_len(nrow(data)). This supports bootstrap optimism correction.oob: integer out-of-bag row indices. These are computed assort(setdiff(seq_len(nrow(data)), unique(analysis))).seed: single positive integer for reproducible model fits on this split.
Bootstrap assignment
Rows are the sampling units when group is NULL.
Groups are the sampling units when group is supplied.
Each bootstrap split advances the current random-number generator state.
Stratification
Stratification is optional.
Pass NULL for no stratification.
strata = ~ 1 stratifies by the model outcome.
A formula such as strata = ~ a + b stratifies by interaction(data$a, data$b, drop = TRUE).
Variables referenced by strata must be columns of data.
NA stratum values are not allowed.
Empty factor levels are dropped.
Stratification variables are treated as categorical.
Pre-bin continuous variables before passing them to strata.
This also applies when strata = ~ 1 and the model outcome is continuous.
Expressions in strata are not evaluated as transformations.
Create transformed or composite strata columns in data before calling resample_bootstrap().
For row-level bootstrap resampling, stratification samples rows with replacement within each stratum.
It preserves the original row count of each stratum in each bootstrap split.
The analysis vector concatenates per-stratum draws in factor-level order.
Each bootstrap stratum must contain at least two rows.
Grouping
Grouping is optional.
Pass NULL for ordinary row-level resampling.
Pass a one-sided formula such as group = ~ subject to draw whole groups with replacement.
The right-hand side of group must be a single bare column name in data.
NA group values are not allowed.
Empty factor levels are dropped.
When group is supplied, groups are drawn with replacement.
For each bootstrap split, n_groups groups are drawn and their rows are concatenated in draw order.
assessment remains seq_len(nrow(data)).
oob is the set of rows whose group was never drawn into analysis.
Stratified grouped bootstrap draws groups within each stratum to preserve group counts per stratum.
Strata are processed in resolved factor-level order.
Rows are concatenated in draw order within each stratum.
This preserves group counts per stratum rather than row counts per stratum.
When group sizes differ, total analysis length and per-stratum row counts can vary across bootstrap splits.
Each grouped bootstrap stratum must contain at least two groups.
When strata and group are both supplied, the resolved stratum vector must be constant within each group.
If strata = ~ 1 is used with group, the outcome must be constant within each group.
Split Names
Split names are generated from n_boot.
For fewer than 10 splits, names are split1, ..., split9.
For 10 or more splits, names are zero-padded so lexical order matches numeric order.
Reproducibility
Call base::set.seed() immediately before resample_bootstrap() for reproducible splits.
Split indices reproduce the resampled rows, but a downstream model fit that uses randomness depends on the global random-number state when it runs, so it does not reproduce on its own.
Each split therefore carries a seed, a single positive integer generated at construction.
Set the split seed immediately before fitting that split to make the fit reproducible regardless of run order.
# Single fit per split.
set.seed(split$seed)
fit <- fit_model(split$analysis)
# Several candidate models on one analysis set, each reproducible alone.
set.seed(split$seed)
candidate_seeds <- sample.int(.Machine$integer.max, K, replace = FALSE)
for (k in seq_len(K)) {
set.seed(candidate_seeds[k])
fit_k <- fit_candidate_k(split$analysis)
}Seeds are created from the global generator and the generator state is then restored, so split indices and the caller's random-number stream are unchanged.
Reproducing a fit assumes the same base::RNGkind() on each run.
Examples
#----------------------------------------------------------------------------
# resample_bootstrap() examples
#----------------------------------------------------------------------------
library(bkmodel)
set.seed(1L)
data <- data.frame(
outcome = c(rep("event", 12L), rep("none", 12L)),
age = c(34L, 52L, 41L, 63L, 29L, 48L, 37L, 55L,
46L, 31L, 60L, 43L, 39L, 50L, 28L, 44L,
57L, 36L, 62L, 33L, 47L, 53L, 40L, 58L),
clinic = rep(c("north", "south", "west"), each = 8L),
patient = rep(seq_len(12L), each = 2L)
)
bs <- resample_bootstrap(data, outcome ~ age, n_boot = 10L)
bs$splits[[1L]]$analysis
#> [1] 4 7 1 2 23 11 14 18 19 1 21 21 10 22 14 10 7 9 15 21 5 9 14 5
bs$splits[[1L]]$oob
#> [1] 3 6 8 12 13 16 17 20 24
# Preserve the outcome class counts in each bootstrap sample.
resample_bootstrap(data, outcome ~ age, n_boot = 10L, strata = ~ outcome)
#> $call
#> resample_bootstrap(data = data, formula = outcome ~ age, n_boot = 10L,
#> strata = ~outcome)
#>
#> $method
#> [1] "bootstrap"
#>
#> $n
#> [1] 24
#>
#> $formula
#> outcome ~ age
#> <environment: 0x5ed28b9aa680>
#>
#> $outcome_var
#> [1] "outcome"
#>
#> $strata
#> ~outcome
#> <environment: 0x5ed28b9aa680>
#>
#> $strata_vars
#> [1] "outcome"
#>
#> $group
#> NULL
#>
#> $group_vars
#> NULL
#>
#> $n_groups
#> NULL
#>
#> $n_boot
#> [1] 10
#>
#> $splits
#> $splits$split01
#> $splits$split01$analysis
#> [1] 4 3 3 9 9 7 9 5 12 7 12 4 16 22 24 20 13 24 24 24 22 14 22 13
#>
#> $splits$split01$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split01$oob
#> [1] 1 2 6 8 10 11 15 17 18 19 21 23
#>
#> $splits$split01$seed
#> [1] 1940607198
#>
#>
#> $splits$split02
#> $splits$split02$analysis
#> [1] 1 12 4 5 5 6 9 8 5 12 1 2 13 20 17 20 22 19 19 23 23 14 21 16
#>
#> $splits$split02$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split02$oob
#> [1] 3 7 10 11 15 18 24
#>
#> $splits$split02$seed
#> [1] 420024478
#>
#>
#> $splits$split03
#> $splits$split03$analysis
#> [1] 2 5 11 2 4 11 3 6 9 12 7 5 24 17 23 23 13 13 22 13 15 22 19 16
#>
#> $splits$split03$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split03$oob
#> [1] 1 8 10 14 18 20 21
#>
#> $splits$split03$seed
#> [1] 813738758
#>
#>
#> $splits$split04
#> $splits$split04$analysis
#> [1] 1 11 9 7 6 4 2 3 10 6 12 7 13 13 19 13 15 23 19 14 17 24 22 22
#>
#> $splits$split04$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split04$oob
#> [1] 5 8 16 18 20 21
#>
#> $splits$split04$seed
#> [1] 1737510204
#>
#>
#> $splits$split05
#> $splits$split05$analysis
#> [1] 8 2 4 8 9 6 1 8 9 9 11 6 20 15 15 15 13 16 16 24 13 20 17 17
#>
#> $splits$split05$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split05$oob
#> [1] 3 5 7 10 12 14 18 19 21 22 23
#>
#> $splits$split05$seed
#> [1] 1858876342
#>
#>
#> $splits$split06
#> $splits$split06$analysis
#> [1] 10 12 3 9 7 5 3 4 9 7 3 3 16 18 21 21 18 15 24 14 24 19 23 16
#>
#> $splits$split06$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split06$oob
#> [1] 1 2 6 8 11 13 17 20 22
#>
#> $splits$split06$seed
#> [1] 1255750018
#>
#>
#> $splits$split07
#> $splits$split07$analysis
#> [1] 3 7 1 5 3 7 4 9 9 5 5 3 24 17 20 19 23 23 22 22 14 18 14 17
#>
#> $splits$split07$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split07$oob
#> [1] 2 6 8 10 11 12 13 15 16 21
#>
#> $splits$split07$seed
#> [1] 132111535
#>
#>
#> $splits$split08
#> $splits$split08$analysis
#> [1] 1 1 5 10 8 2 9 9 8 8 9 4 22 16 17 22 24 16 19 20 14 13 24 17
#>
#> $splits$split08$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split08$oob
#> [1] 3 6 7 11 12 15 18 21 23
#>
#> $splits$split08$seed
#> [1] 795078956
#>
#>
#> $splits$split09
#> $splits$split09$analysis
#> [1] 10 9 5 8 9 4 10 6 11 2 5 4 13 18 20 13 15 24 13 15 18 23 20 19
#>
#> $splits$split09$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split09$oob
#> [1] 1 3 7 12 14 16 17 21 22
#>
#> $splits$split09$seed
#> [1] 755292119
#>
#>
#> $splits$split10
#> $splits$split10$analysis
#> [1] 4 3 5 2 5 5 12 3 10 2 7 8 14 22 19 23 13 23 21 20 18 18 15 23
#>
#> $splits$split10$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split10$oob
#> [1] 1 6 9 11 16 17 24
#>
#> $splits$split10$seed
#> [1] 1554343027
#>
#>
#>
#> attr(,"class")
#> [1] "bootstrap_resamples" "resamples"
# Draw whole patients with replacement.
resample_bootstrap(data, outcome ~ age, n_boot = 10L, group = ~ patient)
#> $call
#> resample_bootstrap(data = data, formula = outcome ~ age, n_boot = 10L,
#> group = ~patient)
#>
#> $method
#> [1] "bootstrap"
#>
#> $n
#> [1] 24
#>
#> $formula
#> outcome ~ age
#> <environment: 0x5ed28b9aa680>
#>
#> $outcome_var
#> [1] "outcome"
#>
#> $strata
#> NULL
#>
#> $strata_vars
#> NULL
#>
#> $group
#> ~patient
#> <environment: 0x5ed28b9aa680>
#>
#> $group_vars
#> [1] "patient"
#>
#> $n_groups
#> [1] 12
#>
#> $n_boot
#> [1] 10
#>
#> $splits
#> $splits$split01
#> $splits$split01$analysis
#> [1] 23 24 19 20 1 2 11 12 1 2 23 24 11 12 19 20 3 4 7 8 23 24 9 10
#>
#> $splits$split01$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split01$oob
#> [1] 5 6 13 14 15 16 17 18 21 22
#>
#> $splits$split01$seed
#> [1] 1255568582
#>
#>
#> $splits$split02
#> $splits$split02$analysis
#> [1] 13 14 11 12 5 6 7 8 23 24 23 24 23 24 5 6 7 8 19 20 5 6 3 4
#>
#> $splits$split02$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split02$oob
#> [1] 1 2 9 10 15 16 17 18 21 22
#>
#> $splits$split02$seed
#> [1] 196029820
#>
#>
#> $splits$split03
#> $splits$split03$analysis
#> [1] 11 12 5 6 7 8 7 8 19 20 9 10 9 10 13 14 19 20 9 10 15 16 9 10
#>
#> $splits$split03$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split03$oob
#> [1] 1 2 3 4 17 18 21 22 23 24
#>
#> $splits$split03$seed
#> [1] 248836549
#>
#>
#> $splits$split04
#> $splits$split04$analysis
#> [1] 5 6 19 20 13 14 7 8 13 14 1 2 11 12 5 6 15 16 7 8 7 8 19 20
#>
#> $splits$split04$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split04$oob
#> [1] 3 4 9 10 17 18 21 22 23 24
#>
#> $splits$split04$seed
#> [1] 1472103577
#>
#>
#> $splits$split05
#> $splits$split05$analysis
#> [1] 7 8 9 10 1 2 9 10 11 12 9 10 17 18 21 22 21 22 19 20 23 24 7 8
#>
#> $splits$split05$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split05$oob
#> [1] 3 4 5 6 13 14 15 16
#>
#> $splits$split05$seed
#> [1] 537935038
#>
#>
#> $splits$split06
#> $splits$split06$analysis
#> [1] 21 22 11 12 7 8 17 18 11 12 7 8 15 16 11 12 1 2 15 16 21 22 21 22
#>
#> $splits$split06$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split06$oob
#> [1] 3 4 5 6 9 10 13 14 19 20 23 24
#>
#> $splits$split06$seed
#> [1] 69258874
#>
#>
#> $splits$split07
#> $splits$split07$analysis
#> [1] 11 12 9 10 9 10 1 2 17 18 11 12 3 4 5 6 23 24 15 16 13 14 11 12
#>
#> $splits$split07$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split07$oob
#> [1] 7 8 19 20 21 22
#>
#> $splits$split07$seed
#> [1] 1495784403
#>
#>
#> $splits$split08
#> $splits$split08$analysis
#> [1] 17 18 21 22 5 6 19 20 9 10 17 18 3 4 13 14 19 20 19 20 15 16 21 22
#>
#> $splits$split08$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split08$oob
#> [1] 1 2 7 8 11 12 23 24
#>
#> $splits$split08$seed
#> [1] 147912852
#>
#>
#> $splits$split09
#> $splits$split09$analysis
#> [1] 11 12 5 6 21 22 5 6 3 4 15 16 11 12 13 14 11 12 17 18 23 24 23 24
#>
#> $splits$split09$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split09$oob
#> [1] 1 2 7 8 9 10 19 20
#>
#> $splits$split09$seed
#> [1] 1052722677
#>
#>
#> $splits$split10
#> $splits$split10$analysis
#> [1] 1 2 3 4 17 18 1 2 9 10 21 22 19 20 3 4 9 10 9 10 17 18 9 10
#>
#> $splits$split10$assessment
#> [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#>
#> $splits$split10$oob
#> [1] 5 6 7 8 11 12 13 14 15 16 23 24
#>
#> $splits$split10$seed
#> [1] 760633620
#>
#>
#>
#> attr(,"class")
#> [1] "bootstrap_resamples" "resamples"