In Lesson 1 you built a recipe to clean the lender's data, and in Lesson 2 you built a model spec to predict who defaults. Right now those are two separate objects sitting on your desk. To actually score the test set you have to thread them together by hand: prep the recipe, bake the train data, fit the model, bake the test data the same way, then predict. Every place you re-type a step is a place train and test can quietly drift apart. A workflow bolts the recipe and the model into one object that does the whole thing in a single fit() and a single predict().
By the end of this lesson you will be able to:
Explain why a recipe and a model kept as two loose objects invite a train/test preprocessing mismatch
Bundle them into one workflow and fit the whole pipeline with a single call
Predict on raw new data and let the workflow re-apply the recipe for you, then swap the model with one line
Here is the lender's loan book again, rebuilt right here so this page runs on its own, along with the recipe from Lesson 1 and the model spec from Lesson 2.
RInteractive R
library(recipes)library(parsnip)set.seed(7)n <-240loans <-data.frame( income =round(runif(n, 22000, 98000)), # annual income age =round(runif(n, 21, 60)), employed =round(runif(n, 2, 160)), # months at current job home =factor(sample(c("own", "rent", "mortgage"), n, TRUE)))risk <-with(loans, plogis(-1.1+1.4* (income <45000) +1.0* (employed <20) +0.6* (home =="rent")))loans$defaulted <-factor(ifelse(runif(n) < risk, "yes", "no"))train <- loans[1:180, ] # 180 applicants to learn fromtest <- loans[181:240, ] # 60 held back to predict ontable(train$defaulted)#> no yes#> 114 66
RInteractive R
# Lesson 1's recipe: impute, scale, dummy-code (learned on train)rec <-recipe(defaulted ~ income + age + employed + home, data = train) |>step_impute_median(all_numeric_predictors()) |>step_normalize(all_numeric_predictors()) |>step_dummy(all_nominal_predictors())# Lesson 2's model: a logistic regression, fit by glmspec <-logistic_reg() |>set_engine("glm") |>set_mode("classification")
Now score the test set the by-hand way. It takes five separate steps, and you have to keep them in step with each other every single time.
RInteractive R
prepped <-prep(rec, training = train) # 1. learn medians/means/sds on TRAINtrain_bake <-bake(prepped, new_data = train) # 2. apply them to traintest_bake <-bake(prepped, new_data = test) # 3. apply the SAME numbers to testfit_manual <-fit(spec, defaulted ~ ., data = train_bake) # 4. fit on baked trainpredict(fit_manual, new_data = test_bake) |>head() # 5. predict on baked test#> # A tibble: 6 x 1#> .pred_class#> <fct>#> 1 no#> 2 yes#> 3 no#> 4 no#> 5 no#> 6 no
It works, but look at how much can go wrong. Forget step 3 and the model never sees the test set. Bake the test set with a recipe you accidentally re-prepped on test, and the leakage you closed in Lesson 1 comes right back. Add a model later and you must remember to bake its data too.
Warning
Every hand-typed step is a chance for the test data to travel a different path than the training data. A mismatch here does not throw an error; it silently inflates or wrecks your score. This bookkeeping is exactly what a workflow takes off your hands.