workflowsets workflow_map() in R: Fit All Workflows at Once
The workflowsets workflow_map() function in R fits, resamples, or tunes every workflow in a workflow_set() on the same resamples in one call, returning the set with a populated result column you can rank with rank_results() and plot with autoplot().
workflow_map(ws, "fit_resamples", resamples = folds) # fit each workflow on resamples workflow_map(ws, "tune_grid", resamples = folds, grid = 5) # tune each workflow with grid search workflow_map(ws, "tune_bayes", resamples = folds, iter = 10) # Bayesian tune each workflow workflow_map(ws, "tune_grid", resamples = folds, verbose = TRUE) # log progress per workflow workflow_map(ws, "tune_grid", resamples = folds, seed = 1) # fix the seed for reproducibility workflow_map(ws, "tune_grid", resamples = folds, control = ctrl) # pass tune_grid control rank_results(res, rank_metric = "rmse", select_best = TRUE) # rank the populated set
Need explanation? Read on for examples and pitfalls.
What workflow_map() does
workflow_map() applies one fitting function to every row of a workflow set. It iterates over the info column of a workflow_set tibble, runs the function you named ("fit_resamples", "tune_grid", or "tune_bayes") on each candidate, and stores each fit's tune result in the result column. The set goes in, the same set comes out, with the empty result column now filled.
The mental model is map-over-a-tibble. The workflowsets package keeps the bookkeeping (which preprocessor, which model spec, which id) and workflow_map() does the fitting. You never call fit_resamples() or tune_grid() directly on a workflow set; you name the function as a string and let the map handle the loop.
result column in place and returns the whole tibble, so you can pipe straight into rank_results() or collect_metrics(). You do not get a list of fits back; you get a workflow set with results attached row by row.workflow_map() syntax and arguments
workflow_map() takes a workflow set and the name of a tune function. Everything else passes through to that function via ....
The object argument is the unfit workflow set. The fn argument is a character string naming a function from the tune package; passing the function itself does not work. The seed argument is set once before the loop runs, so every workflow sees the same RNG state and bootstrap order. Everything in ... is forwarded to fn, which is how you supply resamples, grid, metrics, and control without listing them in the workflow_map() signature.
workflow_map() returns the input workflow set with result populated. Each row's result is a tune_results or resample_results object that rank_results(), collect_metrics(), collect_predictions(), and autoplot() know how to read.
Three examples of workflow_map() in action
workflow_map() pays off when you want every candidate scored on the same resamples in one call. The examples below all use mtcars so they run quickly in the browser.
The set is four rows of unfit workflows. The result column is empty list-columns, ready to be filled.
Example 1, fit_resamples across the set. Use this when no model has hyperparameters to tune.
The result column now shows <rsmp[+]>, meaning each row holds a populated resample_results object.
Example 2, tune_grid across the set. Use this when some models have tunable parameters; non-tunable workflows fall back to a single fit per fold.
rank_results() collapses the populated set into one tibble sorted by the metric you name. select_best = TRUE keeps only the best configuration per workflow.
Example 3, autoplot across all candidates. Pipe the fitted set straight into a plot.
The plot shows each workflow as a point with error bars, ordered by the rank metric. Hover, save, or compose it into a multi-panel report like any ggplot.
workflow_map() compared with alternatives
workflow_map() replaces a manual loop over fit_resamples() or tune_grid() calls. Pick by how many candidates you have.
| Approach | What it does | When to use |
|---|---|---|
fit_resamples(wf, resamples) |
Cross-validate one workflow | Single candidate, no tuning |
tune_grid(wf, resamples, grid) |
Tune one workflow | Single candidate with tunable args |
workflow_map(ws, "fit_resamples", resamples) |
Cross-validate every workflow in a set | 2+ candidates, no tuning |
workflow_map(ws, "tune_grid", resamples, grid) |
Tune every workflow in the set | 2+ candidates, some tunable |
Manual lapply() over a list of workflows |
Roll your own loop | Almost never; workflow_map handles bookkeeping |
Use workflow_map() whenever you have two or more candidates you want to score on the same resamples. The set keeps the comparison fair (same folds, same seed, same metrics) without you wiring it up by hand.
workflow_map() is the workflowsets answer to caretEnsemble or a manual caret::train() loop. caret returned a list of fits; workflowsets returns a tibble with the fits stored in a list-column, which keeps the downstream verbs (rank_results, collect_metrics, autoplot) uniform.Common pitfalls
Three mistakes account for most workflow_map() errors.
The first is forgetting that resamples belongs in ..., not in the workflow_map signature. Calling workflow_map(ws, "tune_grid") with no resamples passes nothing to tune_grid() and errors with "argument 'resamples' is missing." The fix is to always pass resamples = folds (and grid if tuning) as named arguments in the ... slot.
The second is mixing tunable and non-tunable workflows under fn = "tune_grid". The non-tunable ones still work; tune treats them as a one-row grid and returns a single resample fit. That is usually fine, but the output shape for those rows differs from a real tuned row, so beware when downstream code assumes every result has tunable parameters.
The third is non-reproducible runs. workflow_map(ws, "tune_grid", resamples = folds) picks a fresh seed each call. To get the same numbers next time, pass seed = <integer> explicitly. Setting set.seed() outside the call does not lock in the result column because the map resets the RNG internally before fitting.
workflow_map(ws, tune_grid, ...) errors because the fn argument expects a character string. Pass "tune_grid" quoted; the dispatch happens inside workflowsets.Try it yourself
Try it: Map fit_resamples() across a workflow set with one linear model and one decision tree on mtcars. Use 3-fold cross-validation and rank by rmse.
Click to reveal solution
Explanation: workflow_set() builds the unfit candidates, workflow_map() fits each on the same 3 folds, and rank_results() collapses the populated result column into a ranked tibble.
Related workflowsets and tidymodels functions
workflow_map() sits in the middle of a small but tightly coupled toolset. Each function below has a dedicated job, and the four together cover the full set-then-fit-then-rank loop.
workflow_set()builds the candidate tibble thatworkflow_map()consumes; see workflowsets workflow_set in R.rank_results()ranks the populated workflow set by a metric.tune_grid()is the per-workflow tuner thatworkflow_map()calls underfn = "tune_grid".fit_resamples()is the per-workflow resampler thatworkflow_map()calls underfn = "fit_resamples".autoplot()forworkflow_setplots ranked metrics with error bars.
External reference: workflowsets package documentation.
FAQ
What does workflow_map() do in R?
workflow_map() applies one fitting function (fit_resamples, tune_grid, or tune_bayes) to every workflow in a workflow_set and stores each fit's tune result in the set's result column. You name the function as a character string and pass that function's arguments (resamples, grid, metrics, control) via .... The set goes in unfit and comes out with the result column populated, ready to feed into rank_results(), collect_metrics(), or autoplot().
How do I fit every workflow in a workflow set on the same resamples?
Pipe the set into workflow_map("fit_resamples", resamples = your_folds, seed = 1). The string "fit_resamples" tells workflow_map which tune function to dispatch to per row. The resamples argument forwards to each call. The seed argument fixes the RNG so every workflow sees the same fold order. The return value is the same workflow set with each row's result slot filled with a resample_results object.
What is the difference between workflow_map() and tune_grid()?
tune_grid() tunes one workflow at a time. workflow_map() is the loop that calls tune_grid() (or fit_resamples(), or tune_bayes()) once per row of a workflow set. Use tune_grid() directly when you have a single candidate workflow; use workflow_map() when you have two or more candidates and want them scored on identical resamples with consistent metrics in one call.
Can workflow_map() run in parallel?
Yes, register a parallel backend with the future package before calling it. library(future); plan(multisession) enables parallel resampling across folds; the gains are largest when each workflow has many folds or a wide tuning grid. The workflowsets package respects the same parallel hooks as tune_grid() itself.
How do I get reproducible results from workflow_map()?
Pass seed = <integer> to workflow_map(). The seed is set once before the loop, so every workflow sees the same bootstrap order and the same fold order. Calling set.seed() outside the call does not lock the result column because workflow_map() resets the RNG internally before fitting.