Feature Engineering with recipes
Define preprocessing steps for normalization, encoding, and imputation.
Feature Engineering with recipes is a free R Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the R Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What Is a Recipe?
In tidymodels, a recipe is a blueprint for preprocessing your data. It records the steps needed to transform raw data into model-ready features — without immediately applying them.
The key function is recipe(outcome ~ ., data = train), which defines the formula and the reference dataset used to estimate any statistics needed during preprocessing.
library(recipes)
library(tidymodels)
# Create a recipe using the training data
rec <- recipe(price ~ ., data = train_data)
print(rec)step_normalize()
step_normalize(all_numeric_predictors()) centers each numeric predictor to mean 0 and scales it to standard deviation 1. This is essential for algorithms sensitive to feature scale, such as logistic regression, SVM, or neural networks.
The mean and SD are computed from the training set and applied consistently to test data via bake().
rec <- recipe(price ~ ., data = train_data) |>
step_normalize(all_numeric_predictors())
# Check what steps are recorded
print(rec)step_dummy()
step_dummy(all_nominal_predictors()) converts categorical (factor/character) variables into numeric dummy (one-hot encoded) columns. By default it creates k-1 columns for a k-level factor, avoiding perfect multicollinearity.
Use one_hot = TRUE for tree-based models that benefit from full one-hot encoding.
rec <- recipe(price ~ ., data = train_data) |>
step_dummy(all_nominal_predictors())
# After prep and bake, factor columns become 0/1 numeric columns
prepped <- prep(rec)
baked <- bake(prepped, new_data = NULL)
names(baked)step_impute_median()
Missing values will cause most model engines to fail. step_impute_median(all_numeric_predictors()) replaces NA values with the median computed from the training set. The median is robust to outliers compared to mean imputation.
For categorical variables, use step_impute_mode() to fill with the most frequent value.
rec <- recipe(price ~ ., data = train_data) |>
step_impute_median(all_numeric_predictors()) |>
step_impute_mode(all_nominal_predictors())
print(rec)step_zv() — Remove Zero Variance
step_zv(all_predictors()) removes any predictor that has only a single unique value (zero variance). Such columns carry no information and can cause errors in some model engines.
For near-zero variance, use step_nzv(all_predictors()), which also removes columns where one value dominates overwhelmingly.
rec <- recipe(price ~ ., data = train_data) |>
step_impute_median(all_numeric_predictors()) |>
step_dummy(all_nominal_predictors()) |>
step_zv(all_predictors()) |>
step_normalize(all_numeric_predictors())
print(rec)prep() — Train the Recipe
prep(recipe) estimates all the parameters required by the steps — for example, the mean/SD for normalization, medians for imputation — using the training data. The result is a prepped recipe that knows all the transformation parameters.
Always prep on training data only to avoid data leakage from the test set.
# Build the full recipe
rec <- recipe(price ~ ., data = train_data) |>
step_impute_median(all_numeric_predictors()) |>
step_dummy(all_nominal_predictors()) |>
step_zv(all_predictors()) |>
step_normalize(all_numeric_predictors())
# Estimate parameters from training data
prepped_rec <- prep(rec)
print(prepped_rec)bake() — Apply to New Data
bake(prepped_recipe, new_data = test_data) applies all preprocessing steps using the already-estimated parameters to any dataset. This ensures transformations are consistent between training and test.
Pass new_data = NULL to apply to the training data that was used during prep().
# Apply recipe to test data
baked_test <- bake(prepped_rec, new_data = test_data)
head(baked_test)
# Apply to training data (same as juice())
baked_train <- bake(prepped_rec, new_data = NULL)
dim(baked_train)juice() — Extract Processed Training Data
juice(prepped_recipe) is a convenience function equivalent to bake(prepped_recipe, new_data = NULL). It returns the preprocessed version of the training data that was used during prep().
Note: juice() is slightly deprecated in favour of bake(prep, new_data = NULL), but you will see it in older tidymodels code.
# juice() extracts the processed training data
train_processed <- juice(prepped_rec)
glimpse(train_processed)
# Equivalent modern approach
train_modern <- bake(prepped_rec, new_data = NULL)
identical(train_processed, train_modern) # TRUECombining Multiple Steps
Steps are applied in the order they are added. A typical production recipe follows this order:
- Imputation (fix NAs first)
- Feature creation (interactions, polynomials)
- Dummy encoding (convert factors)
- Zero-variance removal
- Normalization (scale at the end)
This ordering matters — for example, normalizing before dummy encoding would produce incorrect results.
full_rec <- recipe(sale_price ~ ., data = housing_train) |>
step_impute_median(all_numeric_predictors()) |>
step_impute_mode(all_nominal_predictors()) |>
step_log(sale_price, base = 10) |>
step_other(all_nominal_predictors(), threshold = 0.05) |>
step_dummy(all_nominal_predictors()) |>
step_zv(all_predictors()) |>
step_normalize(all_numeric_predictors())
print(full_rec)Inspecting Prep Results
After calling prep(), you can inspect what each step did using tidy(prepped_rec). Each step has a numeric id, and you can drill into it with tidy(prepped_rec, number = 1) to see the estimated parameters.
prepped_rec <- prep(full_rec)
# View all steps and their status
tidy(prepped_rec)
# Inspect normalization parameters (mean, sd per column)
tidy(prepped_rec, number = 5) # step_normalize is step 5Recipes in Practice
Recipes shine when combined with workflows. Instead of manually calling prep() and bake(), you add the recipe to a workflow and tidymodels handles prep/bake automatically during fit() and predict().
This eliminates a common source of bugs where preprocessing is applied differently at training versus inference time.
library(workflows)
# Recipe + model spec combined in a workflow
wf <- workflow() |>
add_recipe(full_rec) |>
add_model(linear_reg() |> set_engine('lm'))
# fit() internally calls prep() + bake() on training data
fitted_wf <- fit(wf, data = housing_train)
# predict() applies bake() to new data automatically
predictions <- predict(fitted_wf, new_data = housing_test)
head(predictions)Quick Check
What does calling prep(recipe) do in the tidymodels recipes framework?
Recipes Recap
Key takeaways from Feature Engineering with Recipes:
recipe(outcome ~ ., data = train)defines the preprocessing blueprint.step_normalize(),step_dummy(),step_impute_median(),step_zv()are the most commonly used steps.prep()estimates parameters from training data only — never from test data.bake(prepped, new_data)applies the trained recipe to any dataset.- Step order matters: impute → create → encode → remove → scale.
- In production, wrap the recipe in a
workflow()to automate prep/bake during fit and predict.
# Full pipeline in 6 lines
rec <- recipe(target ~ ., data = train) |>
step_impute_median(all_numeric_predictors()) |>
step_dummy(all_nominal_predictors()) |>
step_zv(all_predictors()) |>
step_normalize(all_numeric_predictors())
prepped <- prep(rec)
train_baked <- bake(prepped, new_data = NULL)
test_baked <- bake(prepped, new_data = test)Frequently asked questions
Is the “Feature Engineering with recipes” lesson free?
Yes — the full text of “Feature Engineering with recipes” is free to read here on the web, and the R Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the R Academy course, upgrade to CoddyKit PRO.
What will I learn in “Feature Engineering with recipes”?
Define preprocessing steps for normalization, encoding, and imputation. You practise R Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start R Academy?
No prior experience is required. R Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Feature Engineering with recipes” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this R Academy lesson?
Yes. Every R Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Feature Engineering with recipes
- Model Specifications with parsnip
- Workflows: Combining Recipes and Models
- Resampling and Cross-Validation with rsample