In this document, we will perform a numeric prediction job using the diamonds dataset from ggplot2. As ggplot2 is included in tidymodels, we will need only the tidymodels package.
Code
library(tidymodels)
Warning: package 'tidymodels' was built under R version 4.3.3
Warning: package 'dials' was built under R version 4.3.3
Warning: package 'ggplot2' was built under R version 4.3.3
Warning: package 'infer' was built under R version 4.3.3
Warning: package 'modeldata' was built under R version 4.3.3
Warning: package 'parsnip' was built under R version 4.3.3
Warning: package 'recipes' was built under R version 4.3.3
Warning: package 'rsample' was built under R version 4.3.3
Warning: package 'tune' was built under R version 4.3.3
Warning: package 'workflows' was built under R version 4.3.3
Warning: package 'workflowsets' was built under R version 4.3.3
Warning: package 'yardstick' was built under R version 4.3.3
1 The diamonds Dataset
The dataset contains the prices and other attributes of almost 54,000 diamonds.
Code
data("diamonds")diamonds
# A tibble: 53,940 × 10
carat cut color clarity depth table price x y z
<dbl> <ord> <ord> <ord> <dbl> <dbl> <int> <dbl> <dbl> <dbl>
1 0.23 Ideal E SI2 61.5 55 326 3.95 3.98 2.43
2 0.21 Premium E SI1 59.8 61 326 3.89 3.84 2.31
3 0.23 Good E VS1 56.9 65 327 4.05 4.07 2.31
4 0.29 Premium I VS2 62.4 58 334 4.2 4.23 2.63
5 0.31 Good J SI2 63.3 58 335 4.34 4.35 2.75
6 0.24 Very Good J VVS2 62.8 57 336 3.94 3.96 2.48
7 0.24 Very Good I VVS1 62.3 57 336 3.95 3.98 2.47
8 0.26 Very Good H SI1 61.9 55 337 4.07 4.11 2.53
9 0.22 Fair E VS2 65.1 61 337 3.87 3.78 2.49
10 0.23 Very Good H VS1 59.4 61 338 4 4.05 2.39
# ℹ 53,930 more rows
1.1 The target variable
Our job will be predicting the price variable, thus it is a regression or numerical prediction job. It is always a good idea to examine the target variable.
We can see that the variable is highly right-skewed, meaning that there are many samples with a price higher than the expected if the variable followed a normal law.
In those cases, it is a good idea to use the logarithm of the variable as a predictor. The transformations of the target variable must be done outside the prediction workflow, so we define log_price as:
We observe that log_price has a more adequate distribution for predictive modelling.
1.2 The Features
The features of the dataset are some diamond properties. Features from cut to clarity are set as ordered factors, meaning that factor levels are ordinal variables. This means that we can turn them into ordinal variables, rather than a set of dummies.
The other features are numeric, and have to do with diamond size. carat is a measure of diamond weight, and x,y, z, depth and table are measures of diamond size.
As larger diamonds will also be larger, it is not surprising to find high values of correlation between those variables. Let’s use the corrr package to see them.
Code
library(corrr)
Warning: package 'corrr' was built under R version 4.3.3
`geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = "cs")'
We observe a strong, nonlinear relationship between carat and price. carat can be a better feature than x:z variables, as it has no anomalous zero values, so we will skip them and keep carat as feature.
2 Predicting Diamond Prices
Let’s define the elements of a workflow to predict diamond prices.
2.1 Initial Split
Performing an adequate split of the dataset into train and test sets. Keeping 90% of data for the training set.
# A tibble: 8 × 4
variable type role source
<chr> <list> <chr> <chr>
1 carat <chr [2]> predictor original
2 cut <chr [2]> predictor original
3 color <chr [2]> predictor original
4 clarity <chr [2]> predictor original
5 depth <chr [2]> predictor original
6 table <chr [2]> predictor original
7 price <chr [2]> original predictor original
8 log_price <chr [2]> outcome original
2.3 Models
Define=ing two predictive models:
A ensemble-based model, like boosted trees.
A regression-based model, like regularized regression.
I have chosen a regularized regression and a boosted tree model.
Defining a set of ten folders for the training set, and testing the two models with cross validation using as metrics mean absolute error mae, root of mean squared errors rmse and r squared rsq.
The folds:
Code
set.seed(11)folds <-vfold_cv(training(d_split), v =10)
The metrics:
Code
reg_metrics <-metric_set(mae, rmse, rsq)
Let’s test the two models at the same time with lapply().
Code
d_cv <-lapply(d_wf, \(m) m |>fit_resamples(folds, metrics = reg_metrics) |>collect_metrics())
Warning: package 'glmnet' was built under R version 4.3.3
Warning: package 'xgboost' was built under R version 4.3.3
2.5 Model Decision
Let’s see the results:
Code
d_cv
$reg_regression
# A tibble: 3 × 6
.metric .estimator mean n std_err .config
<chr> <chr> <dbl> <int> <dbl> <chr>
1 mae standard 0.166 10 0.000860 Preprocessor1_Model1
2 rmse standard 0.215 10 0.00157 Preprocessor1_Model1
3 rsq standard 0.955 10 0.000533 Preprocessor1_Model1
$boosted_trees
# A tibble: 3 × 6
.metric .estimator mean n std_err .config
<chr> <chr> <dbl> <int> <dbl> <chr>
1 mae standard 0.0858 10 0.000314 Preprocessor1_Model1
2 rmse standard 0.110 10 0.000568 Preprocessor1_Model1
3 rsq standard 0.990 10 0.000114 Preprocessor1_Model1
We observe that the boosted trees model is the one that performs best.
3 Final Model
3.1 Training the Model
Training a model for diamond pricing on the whole training set.
Code
model <- d_bt_wf |>fit(training(d_split))
3.2 Performance of log_price
Evaluating the performance of the model in the test set.
Code
model |>predict(testing(d_split)) |>bind_cols(testing(d_split)) |>reg_metrics(truth = log_price, estimate = .pred)
# A tibble: 3 × 3
.metric .estimator .estimate
<chr> <chr> <dbl>
1 mae standard 0.0878
2 rmse standard 0.113
3 rsq standard 0.989
We can plot target versus prediction:
Code
model |>predict(testing(d_split)) |>bind_cols(testing(d_split)) |>ggplot(aes(log_price, .pred)) +geom_point() +geom_abline(slope =1, intercept =0, color ="red") +theme_minimal()
3.3 Performance with price
Evaluating the performance of the model on the test set using the original price variable and the exponential transformation of the prediction.
We need to obtain the real price using the reverse function of log() that is exp().