Skip to content
AgileLogics
8 min read

Machine Learning with tidymodels

Prerequisites: Generalized linear models, dplyr: mutate, summarise, and group_by

tidymodels is a collection of packages for building machine-learning models with a consistent, tidyverse-style interface — whatever the underlying algorithm.

install.packages("tidymodels")

The workflow

A sound modeling workflow protects you from fooling yourself:

  1. Split the data into training and test sets. The test set is locked away until the very end.
  2. Preprocess with a recipe (scaling, encoding categories, handling missing values), learned from the training data only.
  3. Specify a model with parsnip.
  4. Resample (cross-validation) on the training set to compare and tune models.
  5. Evaluate once on the test set to estimate real-world performance.

An example: predicting penguin species

library(tidymodels)
tidymodels_prefer()

penguins <- modeldata::penguins |> tidyr::drop_na()

set.seed(123)
split <- initial_split(penguins, prop = 0.75, strata = species)
train <- training(split)
test <- testing(split)

rec <- recipe(species ~ bill_length_mm + bill_depth_mm + flipper_length_mm + body_mass_g,
              data = train) |>
  step_normalize(all_numeric_predictors())

model_spec <- rand_forest(trees = 500) |>
  set_engine("ranger") |>
  set_mode("classification")

wf <- workflow() |>
  add_recipe(rec) |>
  add_model(model_spec)

folds <- vfold_cv(train, v = 5, strata = species)
cv_results <- fit_resamples(wf, folds, metrics = metric_set(accuracy, roc_auc))
collect_metrics(cv_results)

final_fit <- last_fit(wf, split)
collect_metrics(final_fit)
# A tibble: 2 × 6
  .metric  .estimator  mean     n std_err .config
  <chr>    <chr>      <dbl> <int>   <dbl> <chr>
1 accuracy multiclass 0.980     5 0.00898 Preprocessor1_Model1
2 roc_auc  hand_till  0.999     5 0.00100 Preprocessor1_Model1

(Exact numbers will vary slightly with package versions.)

  • initial_split(..., strata = species) keeps species proportions similar in both sets.
  • The recipe normalizes numeric predictors; because it's part of the workflow, it's re-learned inside each cross-validation fold, preventing data leakage from the validation data.
  • rand_forest() describes the model; set_engine("ranger") picks the implementation. Swapping to logistic_reg(), boost_tree(), or svm_rbf() changes one line.
  • last_fit trains on the full training set and evaluates on the test set — the number to report.

Tuning

Mark hyperparameters with tune() — for example, rand_forest(mtry = tune(), min_n = tune()) — and tune_grid(wf, folds) tries combinations with cross-validation; select_best() picks the winner.

Common mistakes

  • Data leakage — preprocessing (like scaling or imputing) using the whole dataset before splitting, or peeking at the test set while tuning. Performance then looks better than it will be in reality.
  • Chasing accuracy without checking what the model gets wrong, or whether it's fair across groups.