Get started with additive

Hamada S. Badr

2024-04-28

library(additive)
library(recipes)
library(workflows)

Let’s simulate a data using mgcv package, which is automatically loaded by additive.

set.seed(2020)
dat <- gamSim(1, n = 400, dist = "normal", scale = 2)
## Gu & Wahba 4 term additive model

In a first step, we use the recipes package to prepare (a recipe for) the data.

test_recipe <- dat |>
  recipe() |>
  update_role(y, new_role = "outcome") |>
  update_role(x0, x1, x2, x3, new_role = "predictor") |>
  step_normalize(all_numeric_predictors())
print(test_recipe)
## 
## ── Recipe ──────────────────────────────────────────────────────────────────────
## 
## ── Inputs
## Number of variables by role
## outcome:         1
## predictor:       4
## undeclared role: 5
## 
## ── Operations
## • Centering and scaling for: all_numeric_predictors()

Above, we not only define the roles of the relevant variables but also normalized all numeric predictors to facilitate model fitting later on. In the next step, we use additive to set up a basic model structure.

test_model <- additive(
    family = gaussian(),
    method = "REML"
  ) |>
  set_engine("mgcv") |>
  set_mode("regression")
print(test_model)
## Generalized Additive Model (GAM) Specification (regression)
## 
## Main Arguments:
##   family = gaussian()
##   method = REML
## 
## Computational engine: mgcv

The additive function is the main function of the package to initialize a Generalized Additive Model (GAM). We can set up a lot of the information directly within the function or update the information later on, via the update method. For example, if we didn’t specify the family initially or set it to something else that we now wanted to change, we could use the update method as follows

test_model <- test_model |>
  update(family = gaussian())

Next, we define a workflow via the workflows package, by combining the above defined data processing recipe and the model plus the actual model formula to be passed to the mgcv engine.

test_workflow <- workflow() |>
  add_recipe(test_recipe) |>
  add_model(
    spec = test_model,
    formula = y ~ s(x0) + s(x1) + s(x2) + s(x3)
  )
print(test_workflow)
## ══ Workflow ════════════════════════════════════════════════════════════════════
## Preprocessor: Recipe
## Model: additive()
## 
## ── Preprocessor ────────────────────────────────────────────────────────────────
## 1 Recipe Step
## 
## • step_normalize()
## 
## ── Model ───────────────────────────────────────────────────────────────────────
## Generalized Additive Model (GAM) Specification (regression)
## 
## Main Arguments:
##   family = gaussian()
##   method = REML
## 
## Computational engine: mgcv

We are now ready to fit the model by calling the fit method with the data set we want to train the model on.

test_workflow_fit <- test_workflow |>
  fit(data = dat)
print(test_workflow_fit)
## ══ Workflow [trained] ══════════════════════════════════════════════════════════
## Preprocessor: Recipe
## Model: additive()
## 
## ── Preprocessor ────────────────────────────────────────────────────────────────
## 1 Recipe Step
## 
## • step_normalize()
## 
## ── Model ───────────────────────────────────────────────────────────────────────
## 
## Family: gaussian 
## Link function: identity 
## 
## Formula:
## y ~ s(x0) + s(x1) + s(x2) + s(x3)
## 
## Estimated degrees of freedom:
## 4.24 3.25 8.26 2.22  total = 18.98 
## 
## REML score: 859.5808

To extract the parsnip model fit from the workflow

test_fit <- test_workflow_fit |>
  extract_fit_parsnip()

The gamObject object can be extracted as follows

gam_fit <- test_workflow_fit |>
  extract_fit_engine()
class(gam_fit)
## [1] "gam" "glm" "lm"

We can use the trained workflow, which includes the fitted model, to conveniently predict using new data without having to worry about all the data reprocessing, which is automatically applied using the workflow preprocessor (recipe).

newdata <- dat[1:5, ]
test_workflow_fit |>
  predict(
    new_data = newdata,
    type = "conf_int",
    level = 0.95
  )
## # A tibble: 5 × 2
##   .pred_lower .pred_upper
##     <dbl[1d]>   <dbl[1d]>
## 1        2.60        4.45
## 2        4.90        6.48
## 3        8.74       10.5 
## 4        4.89        6.40
## 5        2.97        4.57

To add the standard errors on the scale of the linear predictors

test_workflow_fit |>
  predict(
    new_data = newdata,
    type = "conf_int",
    level = 0.95,
    std_error = TRUE
  )
## # A tibble: 5 × 3
##   .pred_lower .pred_upper .std_error
##     <dbl[1d]>   <dbl[1d]>  <dbl[1d]>
## 1        2.60        4.45      0.470
## 2        4.90        6.48      0.401
## 3        8.74       10.5       0.457
## 4        4.89        6.40      0.383
## 5        2.97        4.57      0.408