Super learning with functional programming!
Usage
super_learner(
data,
learners,
formulas,
y_variable = NULL,
n_folds = 5,
determine_super_learner_weights = NULL,
ensemble_or_discrete = c("ensemble", "discrete"),
cv_schema,
outcome_type = c("continuous", "binary", "density", "multiclass"),
extra_learner_args = NULL,
cluster_ids = NULL,
strata_ids = NULL,
weights = NULL,
rowids = NULL,
use_complete_cases = FALSE,
train_on_whole_dataset = TRUE
)Arguments
- data
Data to use in training a
super_learner.- learners
A list of predictor/closure-returning-functions. See Details.
- formulas
Either a single regression formula or a vector of regression formulas.
- y_variable
Typically
y_variablecan be inferred automatically from theformulas, but if needed, the y_variable can be specified explicitly.- n_folds
The number of cross-validation folds to use in constructing the
super_learner. Must be a scalar integer of value >= 2.- determine_super_learner_weights
A function/method to determine the weights for each of the candidate
learners. The default is to usedetermine_super_learner_weights_nnls.- ensemble_or_discrete
Defaults to
'ensemble', but can be set to'discrete'. Discretesuper_learner()chooses only one of the candidate learners to have weight 1 in the resulting prediction algorithm, whileensemblesuper_learner()combines predictions from 1 or more candidate learners, with respective weights adding up to 1.- cv_schema
A function that takes
data,n_foldsand returns a list containingtraining_dataandvalidation_data, each of which are lists ofn_foldsdata frames.- outcome_type
One of 'continuous', 'binary', 'multiclass', or 'density'.
outcome_typeis used to infer the correctdetermine_super_learner_weightsfunction if it is not explicitly passed.- extra_learner_args
A list of equal length to the
learnerswith additional arguments to pass to each of the specified learners.- cluster_ids
(default: null) If specified, clusters will either be entirely assigned to training or validation (not both) in each cross-validation split.
- strata_ids
(default: null) If specified, strata are balanced across training and validation splits so that strata appear in both the training and validation splits.
- weights
If specified, (per observation) weights are used to indicate that risk minimization across models (i.e., the meta-learning step) should be targeted to higher weight observations.
- rowids
(default: null) If specified, rowids are stored for use in out-of-fold prediction (
$oof_predict()) and can also be used to validate/observe the behavior of the cross-validation folds construction.- use_complete_cases
(default: FALSE) If the
datapassed have any NA or NaN missing data, restrict thedatatodata[complete.cases(data),].- train_on_whole_dataset
(default: TRUE) If
TRUE, after the cross-validation stages complete, each candidate learner is additionally fit on the whole dataset; these whole-dataset fits are combined with the meta-learned weights to form the$predict()function returned. IfFALSE, the whole-dataset fits are skipped entirely, saving roughly1/n_foldsof the total learner-fitting compute. The resulting model retains all out-of-fold prediction interfaces ($oof_predictions,$oof_predict(),$oof_predict_modified(),$oof_predict_fold()) as well asfitted(),residuals(),coef(),summary(),plot(), andcompare_learners(), since these depend only on the per-fold fits and held-out predictions – but$predict()/predict.nadir_sl_model()error informatively and$fit_learnersisNULL. This is intended for workflows in which only out-of-fold predictions, or counterfactual modifications thereof, are needed – e.g. in nuisance estimation for causal inference estimators or simulation studies.
Value
An object of class inheriting from nadir_sl_model. This is an S3 object,
with elements including a $predict(newdata) method, and some information
about the fit model including y_variable, outcome_type, learner_weights,
holdout_predictions and optionally information about any errors or
warnings thrown by the learner fitting process. holdout_predictions
rows are in cross-validation fold order, while the .sl_rowid column
maps each row to its row index in the input data (after any complete-case
filtering is applied if use_complete_cases is enabled). If any
learners signaled warnings, they are captured (not printed) and returned in
$warnings_from_training_cv_stage1,
$warnings_from_predicting_cv_stage2, and
$warnings_from_training_on_entire_data, each a list of warning
conditions named by learner with user-legible $calls;
$warning_learners lists the learners that warned. If
train_on_whole_dataset = FALSE was used, $fit_learners is
NULL and $predict() errors informatively; see that
parameter's documentation.
Details
The goal of any super learner is to use cross-validation and a set of candidate learners to 1) evaluate how the learners perform on held out data and 2) to use that evaluation to produce a weighted average (for continuous super learner) or to pick a best learner (for discrete super learner) of the specified candidate learners.
Super learner and its statistically desirable properties have been written about at length, including at least the following references:
nadir::super_learner adopts several user-interface design-perspectives
that will be useful to know in understanding what it does and how it works:
The specification of learners should be very flexible, really only constrained by the fact that candidate learners should be designed for the same prediction problem but their details can wildly vary from learner to learner.
It should be easy to specify a customized or new learner.
nadir::super_learner at its core accepts data,
a formula (a single one passed to formulas is fine),
and a list of learners.
learners are taken to be lists of functions of the following specification:
a learner must accept a
dataandformulaargument,a learner may accept more arguments, and
a learner must return a prediction function that accepts
newdataand produces a vector of prediction values givennewdata.
In essence, a learner is specified to be a function taking (data,
formula, ...) and returning a closure (see
http://adv-r.had.co.nz/Functional-programming.html#closures for an
introduction to closures) which is a function accepting newdata returning
predictions.
Since many candidate learners will have hyperparameters that should be tuned,
like depth of trees in random forests, or the lambda parameter for glmnet,
extra arguments can be passed to each learner via the extra_learner_args
argument. extra_learner_args should be a list of lists, one list of
extra arguments for each learner. If no additional arguments are needed
for some learners, but some learners you're using do require additional
arguments, you can just put a NULL value into the extra_learner_args.
See the examples.
In order to seamlessly support using features implemented by extensions to
the formula syntax (like random effects formatted like random intercepts or
slopes that use the (age | strata) syntax in lme4 or splines like s(age | strata) in mgcv), we allow for the formulas argument to either be one
fixed formula that super_learner will use for all the models, or a vector
of formulas, one for each learner specified. Most learners receive the
formula directly, but some learners specifically require matrix input.
nadir automatically does the conversion of formula and data to matrix
input for those learners like lnr_glmnet, lnr_hal,
lnr_xgboost and others.
Note that in the examples a mean-squared-error (mse) is calculated on
the same training/test set, and this is only useful as a crude diagnostic to
see that super_learner is working. A more rigorous performance metric to
evaluate super_learner on is the cv-rmse produced by cv_super_learner.
Examples
# most basic usage:
sl <- super_learner(mtcars, list(lm = lnr_lm, mean = lnr_mean),
mpg ~ hp + wt, n_folds = 2)
coef(sl) # ensemble weights
#> lm mean
#> 0.92388493 0.07611507
head(fitted(sl))
#> [1] 23.77383 22.64693 23.97326 21.14440 17.71882 20.18613
head(residuals(sl))
#> [1] -2.7738289 -1.6469301 -1.1732642 0.2556017 0.9811792 -2.0861350
formula(sl)
#> mpg ~ hp + wt
#> <environment: 0x3232f6860>
nobs(sl)
#> [1] 32
summary(sl)
#> Summary of Super Learner fit
#> outcome: mpg (continuous); n = 32; folds = 2
#>
#> learner weight cv_holdout_loss
#> lm 0.9239 8.113
#> mean 0.0761 39.580
#>
#> Loss is evaluated on held-out (cross-validation) data; lower is better.
# here's a slightly more in depth example with fancy formula features.
# note all these packages (glmnet, lme4, randomForest) are highly
# Suggested (among others) with {nadir}.
if (requireNamespace("glmnet", quietly = TRUE) &&
requireNamespace("lme4", quietly = TRUE) &&
requireNamespace("randomForest", quietly = TRUE)) {
learners <- list(
glm = lnr_glm,
rf = lnr_rf,
glmnet = lnr_glmnet,
lmer = lnr_lmer
)
# mtcars example ---
formulas <- c(
.default = mpg ~ cyl + hp, # first three models use same formula
lmer = mpg ~ (1 | cyl) + hp # lme4 uses different language features
)
# fit a super_learner
sl_model <- super_learner(
data = mtcars,
formula = formulas,
learners = learners
)
# We recommend taking a look at this object to see what's contained inside it:
sl_model
compare_learners(sl_model)
# iris example ---
sl_model <- super_learner(
data = iris,
formula = list(
.default = Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width,
lmer = Sepal.Length ~ (Sepal.Width | Species) + Petal.Length
),
learners = learners
)
# produce super_learner predictions and compare against the individual learners
compare_learners(sl_model)
}
#> Inferring the loss metric for learner comparison based on the outcome type:
#> outcome_type=continuous -> using mean squared error
#> boundary (singular) fit: see help('isSingular')
#> boundary (singular) fit: see help('isSingular')
#> Inferring the loss metric for learner comparison based on the outcome type:
#> outcome_type=continuous -> using mean squared error
#> # A tibble: 1 × 4
#> glm rf glmnet lmer
#> <dbl> <dbl> <dbl> <dbl>
#> 1 0.101 0.129 0.207 0.1000
