rtidymodelsr-recipes

What is the difference among prep/bake/juice in the R package "recipes"?


I read the introduction to tidymodels and I am confused about what prep(), bake() and juice() from the recipes package do to the data. What each does? I honestly found confusing to have such names for functions, what would be a more intuitive name for each one out of the culinary analogy?


Solution

  • Let's walk through what each of these functions does. First, let's define a recipe with a couple of steps. Notice that the data going into the recipe is the training data.

    library(recipes)
    
    cars_train <- mtcars[1:20,]
    cars_test <- mtcars[21:32,]
    
    cars_rec <- recipe(mpg ~ ., data = cars_train) %>%
      step_log(disp) %>%
      step_center(all_predictors())
    cars_rec
    #> Data Recipe
    #> 
    #> Inputs:
    #> 
    #>       role #variables
    #>    outcome          1
    #>  predictor         10
    #> 
    #> Operations:
    #> 
    #> Log transformation on disp
    #> Centering for all_predictors
    

    The preprocessing recipe cars_rec has been defined but no values have been estimated. For example, the log has not been taken for disp, and the mean has not been calculated for predictors so that they can be centered.

    The prep() function takes that defined object and computes everything so that the preprocessing steps can be executed. For example, the mean of each predictor is calculated in this example so the predictors can be centered. This is done with the training data.

    cars_prep <- prep(cars_rec)
    cars_prep
    #> Data Recipe
    #> 
    #> Inputs:
    #> 
    #>       role #variables
    #>    outcome          1
    #>  predictor         10
    #> 
    #> Training data contained 20 data points and no missing data.
    #> 
    #> Operations:
    #> 
    #> Log transformation on disp [trained]
    #> Centering for cyl, disp, hp, drat, wt, qsec, vs, am, ... [trained]
    

    Notice that before, with the unprepped recipe, it just said Centering for all_predictors because it had not been evaluated yet. Now it has been evaluated and we know which columns are predictors and what their means are.

    The bake() and juice() functions both return data, not a preprocessing recipe object. The bake() function takes a prepped recipe (one that has had all quantities estimated from training data) and applies it to new_data. That new_data could be the training data again...

    bake(cars_prep, new_data = cars_train)
    #> # A tibble: 20 x 11
    #>      cyl   disp    hp   drat      wt   qsec    vs    am  gear   carb   mpg
    #>    <dbl>  <dbl> <dbl>  <dbl>   <dbl>  <dbl> <dbl> <dbl> <dbl>  <dbl> <dbl>
    #>  1  -0.2 -0.222 -26.2  0.355 -0.778  -1.98   -0.5   0.7   0.5  1.30   21  
    #>  2  -0.2 -0.222 -26.2  0.355 -0.523  -1.42   -0.5   0.7   0.5  1.30   21  
    #>  3  -2.2 -0.615 -43.2  0.305 -1.08    0.169   0.5   0.7   0.5 -1.7    22.8
    #>  4  -0.2  0.256 -26.2 -0.465 -0.183   0.999   0.5  -0.3  -0.5 -1.7    21.4
    #>  5   1.8  0.589  38.8 -0.395  0.0415 -1.42   -0.5  -0.3  -0.5 -0.7    18.7
    #>  6  -0.2  0.119 -31.2 -0.785  0.0615  1.78    0.5  -0.3  -0.5 -1.7    18.1
    #>  7   1.8  0.589 109.  -0.335  0.172  -2.60   -0.5  -0.3  -0.5  1.30   14.3
    #>  8  -2.2 -0.309 -74.2  0.145 -0.208   1.56    0.5  -0.3   0.5 -0.7    24.4
    #>  9  -2.2 -0.350 -41.2  0.375 -0.248   4.46    0.5  -0.3   0.5 -0.7    22.8
    #> 10  -0.2 -0.176 -13.2  0.375  0.0415 -0.141   0.5  -0.3   0.5  1.30   19.2
    #> 11  -0.2 -0.176 -13.2  0.375  0.0415  0.459   0.5  -0.3   0.5  1.30   17.8
    #> 12   1.8  0.323  43.8 -0.475  0.672  -1.04   -0.5  -0.3  -0.5  0.300  16.4
    #> 13   1.8  0.323  43.8 -0.475  0.332  -0.841  -0.5  -0.3  -0.5  0.300  17.3
    #> 14   1.8  0.323  43.8 -0.475  0.382  -0.441  -0.5  -0.3  -0.5  0.300  15.2
    #> 15   1.8  0.860  68.8 -0.615  1.85   -0.461  -0.5  -0.3  -0.5  1.30   10.4
    #> 16   1.8  0.834  78.8 -0.545  2.03   -0.621  -0.5  -0.3  -0.5  1.30   10.4
    #> 17   1.8  0.790  93.8 -0.315  1.95   -1.02   -0.5  -0.3  -0.5  1.30   14.7
    #> 18  -2.2 -0.932 -70.2  0.535 -1.20    1.03    0.5   0.7   0.5 -1.7    32.4
    #> 19  -2.2 -0.970 -84.2  1.38  -1.78    0.079   0.5   0.7   0.5 -0.7    30.4
    #> 20  -2.2 -1.03  -71.2  0.675 -1.56    1.46    0.5   0.7   0.5 -1.7    33.9
    

    Or it could be the testing data. In this case, the column means from the training data are applied to the testing data, because that is what happens IRL in a modeling workflow. To do otherwise is data leakage.

    bake(cars_prep, new_data = cars_test)
    #> # A tibble: 12 x 11
    #>      cyl   disp    hp     drat      wt   qsec    vs    am  gear  carb   mpg
    #>    <dbl>  <dbl> <dbl>    <dbl>   <dbl>  <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
    #>  1  -2.2 -0.509 -39.2  0.155   -0.933   1.57    0.5  -0.3  -0.5 -1.7   21.5
    #>  2   1.8  0.465  13.8 -0.785    0.122  -1.57   -0.5  -0.3  -0.5 -0.7   15.5
    #>  3   1.8  0.420  13.8 -0.395    0.0366 -1.14   -0.5  -0.3  -0.5 -0.7   15.2
    #>  4   1.8  0.561 109.   0.185    0.442  -3.03   -0.5  -0.3  -0.5  1.30  13.3
    #>  5   1.8  0.694  38.8 -0.465    0.447  -1.39   -0.5  -0.3  -0.5 -0.7   19.2
    #>  6  -2.2 -0.928 -70.2  0.535   -1.46    0.459   0.5   0.7   0.5 -1.7   27.3
    #>  7  -2.2 -0.507 -45.2  0.885   -1.26   -1.74   -0.5   0.7   1.5 -0.7   26  
    #>  8  -2.2 -0.742 -23.2  0.225   -1.89   -1.54    0.5   0.7   1.5 -0.7   30.4
    #>  9   1.8  0.564 128.   0.675   -0.228  -3.94   -0.5   0.7   1.5  1.30  15.8
    #> 10  -0.2 -0.320  38.8  0.075   -0.628  -2.94   -0.5   0.7   1.5  3.3   19.7
    #> 11   1.8  0.410 199.  -0.00500  0.172  -3.84   -0.5   0.7   1.5  5.3   15  
    #> 12  -2.2 -0.501 -27.2  0.565   -0.618   0.159   0.5   0.7   0.5 -0.7   21.4
    

    The juice() function is a nice little shortcut. Because the prepped recipe was estimated from the training data, you can process the training data only from it. Picture yourself squeezing the prepped recipe to get the training data back out that you used to estimate the preprocessing parameters with to start with.

    juice(cars_prep)
    #> # A tibble: 20 x 11
    #>      cyl   disp    hp   drat      wt   qsec    vs    am  gear   carb   mpg
    #>    <dbl>  <dbl> <dbl>  <dbl>   <dbl>  <dbl> <dbl> <dbl> <dbl>  <dbl> <dbl>
    #>  1  -0.2 -0.222 -26.2  0.355 -0.778  -1.98   -0.5   0.7   0.5  1.30   21  
    #>  2  -0.2 -0.222 -26.2  0.355 -0.523  -1.42   -0.5   0.7   0.5  1.30   21  
    #>  3  -2.2 -0.615 -43.2  0.305 -1.08    0.169   0.5   0.7   0.5 -1.7    22.8
    #>  4  -0.2  0.256 -26.2 -0.465 -0.183   0.999   0.5  -0.3  -0.5 -1.7    21.4
    #>  5   1.8  0.589  38.8 -0.395  0.0415 -1.42   -0.5  -0.3  -0.5 -0.7    18.7
    #>  6  -0.2  0.119 -31.2 -0.785  0.0615  1.78    0.5  -0.3  -0.5 -1.7    18.1
    #>  7   1.8  0.589 109.  -0.335  0.172  -2.60   -0.5  -0.3  -0.5  1.30   14.3
    #>  8  -2.2 -0.309 -74.2  0.145 -0.208   1.56    0.5  -0.3   0.5 -0.7    24.4
    #>  9  -2.2 -0.350 -41.2  0.375 -0.248   4.46    0.5  -0.3   0.5 -0.7    22.8
    #> 10  -0.2 -0.176 -13.2  0.375  0.0415 -0.141   0.5  -0.3   0.5  1.30   19.2
    #> 11  -0.2 -0.176 -13.2  0.375  0.0415  0.459   0.5  -0.3   0.5  1.30   17.8
    #> 12   1.8  0.323  43.8 -0.475  0.672  -1.04   -0.5  -0.3  -0.5  0.300  16.4
    #> 13   1.8  0.323  43.8 -0.475  0.332  -0.841  -0.5  -0.3  -0.5  0.300  17.3
    #> 14   1.8  0.323  43.8 -0.475  0.382  -0.441  -0.5  -0.3  -0.5  0.300  15.2
    #> 15   1.8  0.860  68.8 -0.615  1.85   -0.461  -0.5  -0.3  -0.5  1.30   10.4
    #> 16   1.8  0.834  78.8 -0.545  2.03   -0.621  -0.5  -0.3  -0.5  1.30   10.4
    #> 17   1.8  0.790  93.8 -0.315  1.95   -1.02   -0.5  -0.3  -0.5  1.30   14.7
    #> 18  -2.2 -0.932 -70.2  0.535 -1.20    1.03    0.5   0.7   0.5 -1.7    32.4
    #> 19  -2.2 -0.970 -84.2  1.38  -1.78    0.079   0.5   0.7   0.5 -0.7    30.4
    #> 20  -2.2 -1.03  -71.2  0.675 -1.56    1.46    0.5   0.7   0.5 -1.7    33.9
    

    Created on 2020-06-04 by the reprex package (v0.3.0)

    It is the same as bake(cars_prep, new_data = cars_train) and is just a shortcut.