Skip to content

nested_resamples() builds a nested resampling design: one row per outer fold, with that fold's inner resamples beside it. It gives the same splits as rsample::nested_cv(), without a copy of the data for each outer fold.

Usage

nested_resamples(data, outside, inside, ...)

Arguments

data

A data frame.

outside

The outer resampling, as an unevaluated call such as vfold_cv(v = 5) or as an rset already built on data.

inside

The inner resampling, as an unevaluated call such as vfold_cv(v = 5). It is evaluated on each outer fold's analysis set, so an rset is an error.

...

Not used, and must be empty.

Value

An object of class nested_resamples, which also carries the classes rsample::nested_cv() returns. It is the outer rset with an inner_resamples column, one inner rset per outer split.

Differences from rsample

rsample::analysis() and rsample::assessment() return the same frames as on rsample's splits, and each inner split keeps rsample's class and id. These designs are an error, whether nested_resamples() or rsample::nested_cv() built them:

A design is known by the class of its splits as well as its own class, so a design rebuilt with rsample::manual_rset() is caught too. An inner rsample::validation_set() cannot be built, because it takes a split and not a data frame.

A single split is an error in either loop if its assessment set holds a row of its analysis set. Inside an outer fold, two copies of one data row count as the same row. The apparent split of a bootstrap with the id "Apparent" is exempt, because tune leaves it out of its estimates. For the same reason, an inner split with the id "Apparent" is an error unless it is that split. So is an inner split with a missing id, or two inner splits with the same values in every id column.

An outer split is an error if its in_id or out_id holds an NA, is not numeric, or holds a value outside integer range. So is an empty out_id. The logical NA out_id, which tells rsample to use rsample::complement(), is accepted.

Time-series designs

Below, the sliding designs are rsample::sliding_window(), rsample::sliding_index() and rsample::sliding_period(), and the four other tuners are nested_tune_bayes(), the two racing functions and nested_tune_sim_anneal(). Each pair of designs below is tested to give the same splits as rsample::nested_cv(). A final fit is tested on the results of the functions named with it.

Any other inner design is not tested. A lag above 0 in rsample::rolling_origin() is an error in either loop, because each assessment set then holds the last lag rows of its analysis set. Build lagged predictors before resampling.

Memory

rsample::nested_cv() keeps a copy of each outer fold's analysis set. nested_resamples() keeps only row indices that point back at data. The sizes below are multiples of the source data. They were measured on mlbench::LetterRecognition (20000 x 17) with five inner folds, under rsample 1.3.2 and R 4.6.1, on 2026-07-25. tests/testthat/test-nested-resamples-memory.R checks how fast each size grows with the number of outer folds.

outer foldsrsample::nested_cv()nested_resamples()
22.2x1.2x
55.6x1.7x
1011.4x2.6x
5057.5x10.0x

See also

rsample::nested_cv(), nested_tune_grid(), which takes the design and runs the outer loop

Examples

data(mtcars)

set.seed(1)
design <- nested_resamples(
  data = mtcars,
  outside = rsample::vfold_cv(v = 3),
  inside = rsample::vfold_cv(v = 3)
)
design
#> # Nested resampling:
#> #  outer: 3-fold cross-validation
#> #  inner: 3-fold cross-validation
#> # A tibble: 3 × 3
#>   splits          id    inner_resamples
#>   <list>          <chr> <list>         
#> 1 <split [21/11]> Fold1 <vfold [3 × 2]>
#> 2 <split [21/11]> Fold2 <vfold [3 × 2]>
#> 3 <split [22/10]> Fold3 <vfold [3 × 2]>

# Each element of inner_resamples is an ordinary rset.
design$inner_resamples[[1]]
#> #  3-fold cross-validation 
#> # A tibble: 3 × 2
#>   splits         id   
#>   <list>         <chr>
#> 1 <split [14/7]> Fold1
#> 2 <split [14/7]> Fold2
#> 3 <split [14/7]> Fold3