Skip to contents

Create 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.

Usage

resample_bootstrap(data, formula, n_boot = 1000L, strata = NULL, group = NULL)

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 in data. Calls such as cbind(y1, y2), Surv(time, status), log(y), and factor(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 as 10 and 10.0 are accepted and stored as integers.

strata

(formula: NULL)
Stratification specification. Use NULL for no stratification. Use ~ 1 to stratify by the model outcome. Use a one-sided formula such as ~ a + b to stratify by the interaction of columns a and b.

group

(formula: NULL)
Grouping specification. Use NULL for row-level resampling. Use a one-sided formula such as ~ subject to draw whole groups with replacement. The right-hand side must be a single bare column name. Create composite group keys in data before calling resample_bootstrap().

Value

An S3 list of class c("bootstrap_resamples", "resamples") with elements:

  • call: the matched call.

  • method: "bootstrap".

  • n: nrow(data).

  • formula: the input formula.

  • outcome_var: the resolved outcome column name.

  • strata: the input strata formula or NULL.

  • strata_vars: the resolved strata column names or NULL.

  • group: the input group formula or NULL.

  • group_vars: the resolved group column name or NULL.

  • n_groups: the number of unique groups when group is supplied, otherwise NULL.

  • n_boot: the resolved bootstrap count.

  • splits: a named list of split entries. Each split contains analysis, assessment, oob, and seed.

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. When group is NULL, length equals nrow(data). When group is supplied, length equals the total row count of n_groups drawn 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 as sort(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"