Skip to Content
Course content

92: Practice Exercise: Building a Predictive Model with tidymodels

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Alright, we've talked about the theory of the tidymodels ecosystem, but it's one thing to read the documentation and another to actually get a model to behave. Let's get our hands dirty. I'm going to use the palmerpenguins dataset. The goal is simple: can we predict the species of a penguin based on its bill length, bill depth, and flipper length?

Getting the data split just right

First, I'll start by splitting the data. I've seen too many people just take the first 80% of a CSV, which is a disaster if the data is sorted. I'll use initial_split().

library(tidymodels)
library(palmerpenguins)

set.seed(123)
penguin_split <- initial_split(penguins, prop = 0.8, strata = species)
train_data <- training(penguin_split)
test_data < - testing(penguin_split)

Notice I added strata = species. I did this because I don't want to accidentally end up with a test set that has zero Gentoo penguins. Stratification ensures the proportions of species stay the same across both sets. If I didn't do this, my accuracy metrics might look great or terrible just because of a lucky (or unlucky) draw.

Cleaning up the inputs with a recipe

Now, I could just throw the train_data into a model, but that's a bad habit. In a real project, you'll have missing values or need to normalize scales. I'll build a recipe. Let's see what happens if I just keep it basic.

penguin_recipe < - recipe(species ~ ., data = train_data) > 
  step_naomit(all_predictors())

Wait, I just realized something. If I use step_naomit() inside a recipe, it can get a bit finicky with how tidymodels handles the data flow. Actually, for this specific dataset, it's cleaner to just filter out the NAs from the original dataframe before the split. I'm adjusting my approach—lesson learned: handle the "hard" removals before the tidymodels pipeline starts, and use the recipe for "soft" transformations.

# Redoing the split after cleaning
penguins_clean < - penguins |> drop_na()
set.seed(123)
penguin_split < - initial_split(penguins_clean, prop = 0.8, strata = species)
train_data < - training(penguin_split)
test_data < - testing(penguin_split)

penguin_recipe < - recipe(species ~ ., data = train_data) > 
  step_normalize(all_numeric_predictors())


I added step_normalize() because while Random Forests don't strictly need it, I'm in the habit of doing it. It makes the model more robust if I decide to swap the engine to something like a Support Vector Machine later.

Picking a model and hitting a snag

I want to use a Random Forest. I'll define the model specification first.

rf_spec < - rand_forest(trees = 100)

Now, if I try to fit() this right now, R is going to scream at me. Why? Because rand_forest() is just a specification—it's like a blueprint. It doesn't actually know which R package should do the heavy lifting. I need to set the engine. I'll use ranger because it's fast.

rf_spec < - rf_spec |> 
  set_engine("ranger") |> 
  set_mode("classification")

I explicitly set the mode to "classification" because we are predicting a category (species), not a number. If I forget this, tidymodels might try to guess based on the data, but being explicit saves me from a headache three hours into a project.

Wiring it all together

Now for the "magic" part: the workflow. Instead of managing the recipe and the model separately, I'll bundle them. This prevents the common mistake of applying a transformation to the test set that wasn't applied to the training set (data leakage).

penguin_wf < - workflow() |> 
  add_recipe(penguin_recipe) |> 
  add_model(rf_spec)

# Fit the model
penguin_fit < - fit(penguin_wf, data = train_data)

Finally, let's see how we actually did. I'll use augment() to add predictions to my test data and then check the accuracy.

results < - augment(penguin_fit, new_data = test_data)

results |> 
  metrics(truth = species, estimate = .pred_class)

Looking at the output, the accuracy is likely near 98-100%. This dataset is almost "too" easy, but the pipeline is now solid. If I wanted to try a different model, I'd only have to change the rf_spec part and re-run the workflow. That's the real power of this approach.




📋 Practical Task

Exercise: Predicting Diamond Quality

Using the diamonds dataset (built into ggplot2), build a predictive model to classify the cut of a diamond based on its physical attributes (carat, cut, color, clarity, depth, table, price, x, y, z).

Your task is to implement the following pipeline using tidymodels:

  • Data Preparation: Split the diamonds dataset into training (75%) and testing (25%) sets, stratifying by the cut variable.
  • The Recipe: Create a recipe that predicts cut. Include a step to normalize all numeric predictors.
  • The Model: Define a Decision Tree model (decision_tree()) using the rpart engine. Set the mode to "classification".
  • The Workflow: Combine the recipe and model into a workflow and fit it using the training data.
  • Evaluation: Use augment() on the test set and calculate the accuracy and Kappa metrics.

Deliverable: A script that outputs the final accuracy of the diamond cut prediction model.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.