Choose the grain inside the training data, and score the whole procedure
Source:R/select_grain.R
select_grain.Rdgrain_ladder() fits every candidate against one fold map and reports the grid, so reading the
best grain off it and quoting that grain's score quotes a number the held-out units helped
choose. This does the choosing inside the training data instead. Within each outer fold the
training units are split again, every candidate is fitted on part of them and scored on the rest,
the best is refitted on the whole outer training set, and the outer test fold is predicted once.
The estimate that comes back is therefore of the procedure including its choice of grain, which
is what an ecologist applying it to a new site would run.
Usage
select_grain(
x,
y,
learners,
folds = NULL,
inner = 5L,
rule = c("argmax", "coarsest_adequate"),
threshold = NULL,
interval = c("variables", "nested_cv"),
repeats = 1L,
response = "presence_absence",
metric = NULL,
compare = NULL,
control = train_control(),
seed = 1L,
verbose = TRUE
)
# S3 method for class 'timesift_selection'
summary(object, ...)Arguments
- x
A
grain_matrix()result, atimesift_set(), or a named list of representations. Its names are the grains being chosen between.- y
The response for the same units.
- learners
A learner, a list of them, or names of registered ones, as
grain_ladder()takes. Named alongside the grains they form the candidate set.- folds
The outer fold map, from
fold_map()or any named integer vector. Built with the defaults offold_map()when not given.- inner
Number of inner folds the selection is made on, or a function of the outer training response returning a fold map for those units. A count deals the inner folds by the grouping the outer fold map carries, so what
grouped_cv()kept whole outside stays whole inside.- rule
How a candidate is chosen from its inner scores.
"argmax"takes the highest."coarsest_adequate"takes the coarsest candidate whose inner score lies within one standard error of the highest, the one-standard-error rule of Breiman, Friedman, Olshen and Stone (1984) and of Hastie, Tibshirani and Friedman (2009, section 7.10) with coarseness in place of model complexity. See Choosing a candidate.- threshold
NULL, or the rule ofdecision_threshold()a presence-absence cut is learned by:"youden", the cut that maximises TSS,"kappa"or"prevalence". See A cut learned inside the training data.- interval
Which interval to report beside the across-variable one, which is always reported:
"variables"for that one alone, or"nested_cv"for an interval for the procedure's risk. See What the interval is for.- repeats
Repetitions of the nested cross-validation, each on its own fold map. The first is the map the estimate was computed on.
- response
Name of the registered response head.
- metric
Name of a registered metric the selection is made on, or
NULLfor the response's own. The estimate is reported under every registered metric whichever this is.- compare
A
grain_ladder()result on the same units, response and outer fold map, whose arms the selected procedure is contrasted against cell by cell.NULLfor no contrast.- control
train_control(), the training settings every neural learner reads, in the inner search and in the refit alike. A learner carrying a control of its own overrides it on the settings that control names.- seed
Seed for the inner splits. Each outer fold splits under
seedplus its own number, so no two outer folds inherit the same inner partition.- verbose
Report each outer fold and what it selected as it runs.
- object
A selection.
- ...
Ignored.
Value
A timesift_selection: a list carrying selected, one row per outer fold with the
candidate it chose, the inner score it chose on, the highest inner score in that fold
(inner_best) and that score's standard error (inner_se); estimate, the nested score under every
registered metric with its standard error across variables; contrast, one
paired_contrast() row against each arm of compare, or NULL; candidates, the set that
was searched; and scores, the per-cell rows of the selected procedure under the selection
metric, in the layout grain_ladder() returns. The held-out prediction of every unit is in
the predictions attribute and the scorable-cell mask in cells. inner holds every
candidate's inner score and standard error in every outer fold. With threshold set, the
estimate carries the score, its interval and the interval's name in interval, one row per
metric and interval. With interval = "nested_cv" it also carries nested_cv, the same rows
with the estimator's own quantities beside them, and final, the procedure fitted on every
unit, whose risk that interval is for. With threshold set, the
estimate carries one further row, tss_inner_cut, the procedure's TSS at the learned cuts;
thresholds holds the cut of every outer fold and variable; and cut_scores the per-cell
rows it is averaged from, in the layout of scores. Both are NULL otherwise.
Details
A candidate is a (grain, learner) pair: the grains are the elements of the representation set,
which is where a grain and the statistic its grains are summarised by are both named, and the
learners are the ones passed. Both are registry entries or objects built by learner(), so a new
grain, a new grain summary or a new candidate model widens the search with no change here.
What the estimate is of: the expected held-out score of the whole pipeline, selection included, on units drawn as these were. What it is not: the score of the winning grain. That is higher, by the amount selection buys itself, and the difference between the two is the quantity this function exists to keep out of a reported number. It also does not say the selected grain is the one a mechanism acts at; it says that grain predicted best on the units the selector saw.
The cost is the ladder's, multiplied by the number of inner folds: v_outer * (v_inner * candidates + 1) fits. With a neural learner that is where an overnight run goes.
Choosing a candidate
Inside each outer fold every candidate carries an inner score, the mean over variables of its
per-variable mean over the inner folds, and a standard error, the standard deviation over the
inner folds of the fold's own score (the mean over the variables scored in that fold) divided by
the square root of the number of inner folds. rule = "argmax" takes the highest inner score,
and on an exact tie the candidate declared first.
rule = "coarsest_adequate" first finds that highest score and its standard error, calls every
candidate scoring at least the highest minus one standard error adequate, and takes the coarsest
adequate one. Coarseness is read off the representation as the package holds it: fewer bins is
coarser, and between two candidates with the same number of bins, fewer channels is coarser.
A tie on both goes to the higher inner score, then to the candidate declared first. Where one
candidate scores more than a standard error above every other, the two rules agree; where the
inner profile is flat, this one returns the least storage the record can be kept at without a
measured loss inside the training data. A standard error that cannot be computed, because a
candidate was scored in fewer than two inner folds, is taken as zero, so the rule falls back to
the candidates tied with the highest score.
What the interval is for
The across-variable interval, the one every level of the package reports, is the estimate plus or minus a Student's t quantile times the standard error across the response variables. Its spread is the spread of true skill between variables, and it cannot see the error every variable shares, since all of them are fitted and scored on the same units and the same folds. It is an interval over the variables of this dataset, and not an interval for what the procedure would score on a new sample.
interval = "nested_cv" adds one that is, by the nested cross-validation of Bates, Hastie and
Tibshirani (2024). Inside every repetition, each outer training set is cross-validated again
over the remaining folds of the same map, which gives the mean squared error of a
cross-validation estimate as the difference of two terms it can estimate: the squared gap
between the inner estimate and the held-out fold's score, less the variance of that fold's
score. The paper's error is a mean of per-unit losses; here a fold's score is the mean over the
variables scorable in it, the inner estimate is averaged as the reported estimate is, and the
variance of a fold's score is its delete-one jackknife variance over the units of the fold,
which for a mean of per-unit losses is exactly the paper's var(e) / |I_k|. The square root of
the estimated mean squared error is held between the jackknife standard error of the estimate
and the square root of the fold count times it, as the paper's section 4.3.2 has it, and the
centre carries its bias correction, so the interval is for the risk of the procedure fitted on
a sample of this size, which is the fit final holds.
The cost is the selection's, multiplied: one repetition fits the procedure once for every
unordered pair of outer folds, v_outer * (v_outer - 1) / 2 fits, and every repetition after
the first refits the outer folds as well. More repetitions steady the estimate of the mean
squared error; the paper uses two hundred random splits, which is affordable where a fit is
cheap and is not where a fit is a neural network.
A cut learned inside the training data
TSS read at the cut that maximises it on the scored units is biased upward, most where presences
are few (tss_inflation()). With threshold set, each outer fold learns one cut per variable
on the inner out-of-fold predictions of the candidate it selected, which the inner search has
already made for every outer training unit, by decision_threshold() under the rule named. The
cut is then frozen and the outer test fold's predictions are read at it by tss(). No unit of
an outer test fold enters the cut its own fold is read at. The inner out-of-fold predictions
come from models fitted on part of the outer training set and the held-out predictions from
the refit on all of it, so the cut is learned on predictions of the same candidate from slightly
smaller training sets.
References
Bates, S., Hastie, T. and Tibshirani, R. (2024). Cross-validation: what does it estimate and how well does it do it? Journal of the American Statistical Association 119(546), 1434-1445. doi:10.1080/01621459.2023.2197686
See also
grain_ladder() for the grid this selects from, and paired_contrast() for the
comparison the contrast element holds.
Examples
set.seed(1)
t <- seq(as.POSIXct("2021-09-01", tz = "UTC"), by = "hour", length.out = 24 * 200)
units <- sprintf("p%02d", 1:60)
warmth <- rnorm(60)
d <- data.frame(
plot = rep(units, each = length(t)), t = rep(t, length(units)),
temp = as.numeric(vapply(warmth, function(w) w + sin(seq_along(t) / 300) + rnorm(length(t)),
numeric(length(t)))))
y <- matrix(rbinom(120, 1, plogis(c(warmth, -warmth))), nrow = 60,
dimnames = list(units, c("sp1", "sp2")))
x <- grain_matrix(d, plot, t, temp, grain = c("week", "month"))
sel <- select_grain(x, y, elasticnet(), folds = fold_map(y, v = 3), inner = 3,
verbose = FALSE)
sel
sel$estimate