Skip to contents

A multimodel combines several models with identical response in one object. There are two ways to create a multimodel:

  • multimodel(x, ...) takes an object of class “model” or a fitted model as its input x and generates a number of variations of this model, with one or several parameters varying according to the specification in “...”.

  • c.model(...) and c.multimodel(...) take a number of models or multimodels as their “...” arguments and combine them in an object of class “multimodel”.

Usage

multimodel(x, ...)

# S3 method for model
multimodel(
  x,
  ...,
  prefix = label(x),
  expand = TRUE,
  max_n_model = getOption("expand_max_model"),
  param = TRUE,
  simplify = TRUE
)

# S3 method for default
multimodel(
  x,
  ...,
  prefix = "model",
  expand = TRUE,
  param = TRUE,
  simplify = TRUE
)

# S3 method for multimodel
multimodel(x, ...)

# S3 method for multimodel
print(
  x,
  what = c("class", "formula", "data", "weights", "call", "cv_info"),
  abbreviate = TRUE,
  n = getOption("print_max_model"),
  param = TRUE,
  width = getOption("width"),
  ...
)

# S3 method for model
c(..., param = TRUE, simplify = TRUE)

# S3 method for multimodel
c(..., param = TRUE, simplify = TRUE)

Arguments

x

An object of class “model” or a fitted model.

...

In multimodel(): named parameters to be expanded; in c.(multi)model: one or several arguments of class “model” or “multimodel”.

prefix

Prefix to use for the model label.

expand

Logical: Expand the “...” arguments (default) or join them element-wise? If expand=TRUE, the vectors in “...” will be expanded, the number of models will equal the product of the lengths of the “...” arguments; otherwise, all “...” arguments must have equal lengths, and the number of models will be equal to their common length.

max_n_model

Maximal number of models that will be included. If the number of specified models is greater than max_n_model, a subset will be selected at random.

param

Logical: Keep or print parameter table?

simplify

Logical: Simplify a multimodel comprising only one model to a model?

what

Which elements of the multimodel should be printed? See print.model.

abbreviate

Logical. If TRUE (the default), long formulas and calls are printed in abbreviated mode, such that they usually fit on 4 or fewer output lines; otherwise they are printed entirely, no matter how long they are.

n

Integer: Model details are printed for first n models.

width

Integer: Width of printed output.

Value

multimodel() and c.(multi)model() both return an object of class “multimodel”, this is a list with the following elements:

  • models: A list of the model objects.

  • param: The table of the parameter values resulting from multimodel(). Only included if there are any varying parameters and if param = TRUE

Details

multimodel() is a (S3-)generic function. The core method is multimodel.model(), other methods are essentially wrappers to the core method and are described in the section “Methods” below. The vectors in “...” are expanded or joined, according to the argument expand. Extraction of values for the single calls is done with “[[” rather than “[”.

multimodel() executes update.model() repeatedly, such that the helper functions absent(), unchanged() and null() can be used in a call to multimodel() exactly as within update.model().

If formula is one of the arguments in the “...” and some formula has a dot, it will be used to update the original model formula. Enclose a formula in I() in order to replace the original formula instead. See the example below and update.model.

The output of multimodel(...) by default includes a parameter table as its element param, containing the values of the parameters specified in the “...”. This is a data.frame having an additional class “param_table”.

c.(multi)model: A call to c(...) can include both “model” and “multimodel” objects (but no fitted models) in its “...”. Using models() instead of these c-methods is more flexible in that it also accepts fitted models.

“model”s specified as named arguments in the call will have their argument name taken as their label. Argument names for multimodels are ignored -- the labels already present in the multimodel will be used instead. Duplicate labels in the output will be adjusted using make.unique().

Methods

  • multimodel.model() is described in the “Details” section.

  • The default method expects a fitted model as its x and executes x %>% model %>% multimodel(...).

  • multimodel.multimodel() returns its input x unchanged.

See also

label, extract_model, subset, sort_models, update.model, expand_formula.

models() is a more general version of c.(multi)model also accepting fitted models.

Examples

m1 <- model(lm(Sepal.Length ~ 1 , iris), label = "intercept")
m2 <- model(lm(Sepal.Length ~ . , iris), label = "linear")
m3 <- model(lm(Sepal.Length ~ .^2 , iris), label = "order2")  # without Species on rhs

# Combine models with c(...)
mm1 <- c(m1, m2, m3) 
mm1
#> --- A “multimodel” object containing 3 models ---
#> 
#> ‘intercept’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ 1
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ 1, data = data)
#> 
#> ‘linear’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + 
#>                     Species
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ ., data = data)
#> 
#> ‘order2’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + 
#>                     Species + Sepal.Width:Petal.Length + Sepal.Width:Petal.Width + 
#>                     Sepal.Width:Species + Petal.Length:Petal.Width + 
#>                     Petal.Length:Species + Petal.Width:Species
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ .^2, data = data)

# Specify elements to print:
print(mm1, what = c("class", "data"))
#> --- A “multimodel” object containing 3 models ---
#> 
#> ‘intercept’:
#>   model class:  lm
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#> 
#> ‘linear’:
#>   model class:  lm
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#> 
#> ‘order2’:
#>   model class:  lm
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
print(mm1, what = TRUE)
#> --- A “multimodel” object containing 3 models ---
#> 
#> ‘intercept’:
#>   label:             intercept
#>   model class:       lm
#>   formula:           Sepal.Length ~ 1
#>   data:              data.frame [150 x 5], 
#>                      input as: ‘data = iris’
#>   response_type:     continuous
#>   response:          Sepal.Length
#>   call:              lm(formula = Sepal.Length ~ 1, data = data)
#>   predict_function:  function (object, ...)  
#>   fit:               Object of class ‘lm’
#> 
#> ‘linear’:
#>   label:             linear
#>   model class:       lm
#>   formula:           Sepal.Length ~ Sepal.Width + Petal.Length + 
#>                          Petal.Width + Species
#>   data:              data.frame [150 x 5], 
#>                      input as: ‘data = iris’
#>   response_type:     continuous
#>   response:          Sepal.Length
#>   call:              lm(formula = Sepal.Length ~ ., data = data)
#>   predict_function:  function (object, ...)  
#>   fit:               Object of class ‘lm’
#> 
#> ‘order2’:
#>   label:             order2
#>   model class:       lm
#>   formula:           Sepal.Length ~ Sepal.Width + Petal.Length + 
#>                          Petal.Width + Species + Sepal.Width:Petal.Length + 
#>                          Sepal.Width:Petal.Width + Sepal.Width:Species + 
#>                          ... [formula cut off - 10 terms on rhs]
#>   data:              data.frame [150 x 5], 
#>                      input as: ‘data = iris’
#>   response_type:     continuous
#>   response:          Sepal.Length
#>   call:              lm(formula = Sepal.Length ~ .^2, data = data)
#>   predict_function:  function (object, ...)  
#>   fit:               Object of class ‘lm’

# Expand a model with multimodel(): random forests with different max.depth:
if (require(ranger)){
  mod_ranger <- model(ranger(Sepal.Length ~ ., iris), label = "ranger")
  multimodel(mod_ranger, max.depth = 1:10)
}
#> --- A “multimodel” object containing 10 models ---
#> 
#> ‘ranger1’:
#>   model class:  ranger
#>   formula:      Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + 
#>                     Species
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         ranger(formula = Sepal.Length ~ ., data = data, 
#>                     max.depth = 1L)
#> 
#> ‘ranger2’:
#>   model class:  ranger
#>   formula:      Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + 
#>                     Species
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         ranger(formula = Sepal.Length ~ ., data = data, 
#>                     max.depth = 2L)
#> 
#> ‘ranger3’:
#>   model class:  ranger
#>   formula:      Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + 
#>                     Species
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         ranger(formula = Sepal.Length ~ ., data = data, 
#>                     max.depth = 3L)
#> 
#> and 7 models more, labelled:
#>   ‘ranger4’, ‘ranger5’, ‘ranger6’, ‘ranger7’, ‘ranger8’, 
#>   ‘ranger9’, ‘ranger10’
#> 
#> 
#> Parameter table:
#>          max.depth
#> ranger1          1
#> ranger2          2
#> ranger3          3
#> ... 7 rows omitted (nrow=10)

#' # Expand a model with multimodel(...)
# Joining models with three formulas may not give the expected result:
mm2 <- multimodel(m1, formula = list(Sepal.Length ~ 1, Sepal.Length ~ ., Sepal.Length ~ .^2))
mm2 
#> --- A “multimodel” object containing 3 models ---
#> 
#> ‘intercept1’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ 1
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ 1, data = data)
#> 
#> ‘intercept2’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ 1
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ 1, data = data)
#> 
#> ‘intercept3’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ 1
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ 1, data = data)
#> 
#> Parameter table:
#>                       formula
#> intercept1 Sepal.Length ~ 1  
#> intercept2 Sepal.Length ~ .  
#> intercept3 Sepal.Length ~ .^2
# The reason is that the formulas in the call above are used to *update* the existing one. 
# To *replace* the formula, use `I()` as in the call below:
mm3 <- multimodel(m1, formula = list(Sepal.Length ~ 1, I(Sepal.Length ~ .), I(Sepal.Length ~ .^2)))
mm3
#> --- A “multimodel” object containing 3 models ---
#> 
#> ‘intercept1’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ 1
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ 1, data = data)
#> 
#> ‘intercept2’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + 
#>                     Species
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ ., data = data)
#> 
#> ‘intercept3’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + 
#>                     Species + Sepal.Width:Petal.Length + Sepal.Width:Petal.Width + 
#>                     Sepal.Width:Species + Petal.Length:Petal.Width + 
#>                     Petal.Length:Species + Petal.Width:Species
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ .^2, data = data)
#> 
#> Parameter table:
#>                       formula
#> intercept1 Sepal.Length ~ 1  
#> intercept2 Sepal.Length ~ .  
#> intercept3 Sepal.Length ~ .^2
# Also note the difference in the printed outputs of mm1 and mm2:
# The parameter table only contains parameters that were passed as '...' in multimodel().

# Three ways to attribute labels to models:
# 1) Use named arguments in c.model():
c(constant = m1, linear = m2, order2 = m3) 
#> --- A “multimodel” object containing 3 models ---
#> 
#> ‘constant’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ 1
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ 1, data = data)
#> 
#> ‘linear’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + 
#>                     Species
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ ., data = data)
#> 
#> ‘order2’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + 
#>                     Species + Sepal.Width:Petal.Length + Sepal.Width:Petal.Width + 
#>                     Sepal.Width:Species + Petal.Length:Petal.Width + 
#>                     Petal.Length:Species + Petal.Width:Species
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ .^2, data = data)
# 2) replacement function label<-
mm1_lbls <- mm1
label(mm1_lbls) <- c("m1", "m2", "m3")
mm1_lbls
#> --- A “multimodel” object containing 3 models ---
#> 
#> ‘m1’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ 1
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ 1, data = data)
#> 
#> ‘m2’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + 
#>                     Species
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ ., data = data)
#> 
#> ‘m3’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + 
#>                     Species + Sepal.Width:Petal.Length + Sepal.Width:Petal.Width + 
#>                     Sepal.Width:Species + Petal.Length:Petal.Width + 
#>                     Petal.Length:Species + Petal.Width:Species
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ .^2, data = data)
# 3) set_labels()
set_label(mm1, c("m1", "m2", "m3"))
#> --- A “multimodel” object containing 3 models ---
#> 
#> ‘m1’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ 1
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ 1, data = data)
#> 
#> ‘m2’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + 
#>                     Species
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ ., data = data)
#> 
#> ‘m3’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + 
#>                     Species + Sepal.Width:Petal.Length + Sepal.Width:Petal.Width + 
#>                     Sepal.Width:Species + Petal.Length:Petal.Width + 
#>                     Petal.Length:Species + Petal.Width:Species
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ .^2, data = data)

# Combine mm1 and mm3:
print(c(mm1, mm3), what = "formula", n = 6)
#> --- A “multimodel” object containing 6 models ---
#> 
#> ‘intercept’:
#>   formula:  Sepal.Length ~ 1
#> 
#> ‘linear’:
#>   formula:  Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + 
#>                 Species
#> 
#> ‘order2’:
#>   formula:  Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + 
#>                 Species + Sepal.Width:Petal.Length + Sepal.Width:Petal.Width + 
#>                 Sepal.Width:Species + Petal.Length:Petal.Width + 
#>                 Petal.Length:Species + Petal.Width:Species
#> 
#> ‘intercept1’:
#>   formula:  Sepal.Length ~ 1
#> 
#> ‘intercept2’:
#>   formula:  Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + 
#>                 Species
#> 
#> ‘intercept3’:
#>   formula:  Sepal.Length ~ Sepal.Width + Petal.Length + Petal.Width + 
#>                 Species + Sepal.Width:Petal.Length + Sepal.Width:Petal.Width + 
#>                 Sepal.Width:Species + Petal.Length:Petal.Width + 
#>                 Petal.Length:Species + Petal.Width:Species
#> 
#> Parameter table:
#>                       formula
#> intercept  <unknown>         
#> linear     <unknown>         
#> order2     <unknown>         
#> intercept1 Sepal.Length ~ 1  
#> intercept2 Sepal.Length ~ .  
#> intercept3 Sepal.Length ~ .^2

# Unweighted and weighted model:
w <- runif(150)
mm3 <- multimodel(m1, weights = list(absent(), w))
label(mm3) <- c("unweighted", "weighted")
mm3
#> --- A “multimodel” object containing 2 models ---
#> 
#> ‘unweighted’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ 1
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   call:         lm(formula = Sepal.Length ~ 1, data = data)
#> 
#> ‘weighted’:
#>   model class:  lm
#>   formula:      Sepal.Length ~ 1
#>   data:         data.frame [150 x 5], 
#>                 input as: ‘data = iris’
#>   weights:      numeric [150], input as: ‘weights = w’
#>   call:         lm(formula = Sepal.Length ~ 1, data = data, weights = weights)
#> 
#> Parameter table:
#>               weights
#> unweighted   <absent>
#> weighted   <num>[150]