1818# ' @param engine A character string for the software that should
1919# ' be used to fit the model. This is highly dependent on the type
2020# ' of model (e.g. linear regression, random forest, etc.).
21+ # ' @param .control A named list with elements `verbosity` and
22+ # ' `catch`. `verbosity` should be an integer where a value of zero
23+ # ' indicates that no messages or output should be shown when
24+ # ' packages are loaded or when the model is fit. A value of 1 means
25+ # ' that package loading is quiet but model fits can produce output
26+ # ' to the screen (depending on if they contain their own
27+ # ' `verbose`-type argument). A value of 2 or more indicates that
28+ # ' any output should be seen. `catch` is a logical where a value of
29+ # ' `TRUE` will evaluate the model inside of `try(, silent = TRUE)`.
30+ # ' If the model fails, an object is still returned (without an
31+ # ' error) that inherits the class "try-error".
2132# ' @param ... Other options required to fit the model. If `x` is a
2233# ' formula or recipe, then the `data` argument should be passed
2334# ' here. For the "x/y" interface, the outcome data should be passed
@@ -48,22 +59,25 @@ fit <- function (object, ...)
4859# ' @return An object for the fitted model.
4960# ' @export
5061# ' @rdname fit
51- fit.model_spec <- function (object , x , engine = object $ engine , ... ) {
62+ fit.model_spec <- function (object , x , engine = object $ engine ,
63+ .control = list (verbosity = 1 , catch = TRUE ),
64+ ... ) {
5265 object $ engine <- engine
5366 object <- check_engine(object )
67+ .control <- check_control(.control )
5468
5569 # sub in arguments to actual syntax for corresponding engine
5670 object <- finalize(object , engine = object $ engine )
5771 check_installs(object )
5872
5973 if (inherits(x , " formula" )) {
60- res <- fit_formula(object , formula = x , ... )
74+ res <- fit_formula(object , formula = x , .control = .control , . .. )
6175 } else {
6276 if (inherits(x , c(" matrix" , " data.frame" ))) {
63- res <- fit_xy(object , x = x , ... )
77+ res <- fit_xy(object , x = x , .control = .control , . .. )
6478 } else {
6579 if (inherits(x , " recipe" )) {
66- res <- fit_recipe(object , recipe = x , ... )
80+ res <- fit_recipe(object , recipe = x , .control = .control , . .. )
6781 } else {
6882 stop(" `x` should be a formula, data frame, matrix, or recipe" )
6983 }
@@ -76,26 +90,25 @@ fit.model_spec <- function(object, x, engine = object$engine, ...) {
7690
7791# ' @importFrom rlang eval_tidy quos
7892# ' @importFrom stats as.formula
79- fit_formula <- function (object , formula , engine = engine , ... ) {
93+ fit_formula <- function (object , formula , engine = engine , .control , . .. ) {
8094 opts <- quos(... )
8195
8296 if (! any(names(opts ) == " data" ))
8397 stop(" Please pass a data frame with the `data` argument." ,
8498 call. = FALSE )
8599
86100 # TODO Should probably just load the namespace
87- for (pkg in object $ method $ library )
88- suppressPackageStartupMessages(library(pkg , character.only = TRUE ))
101+ load_libs(object , .control $ verbosity < 2 )
89102
90103 # Look up the model's interface (e.g. formula, recipes, etc)
91104 # and delagate to the connector functions (`formula_to_recipe` etc)
92105 if (object $ method $ interface == " formula" ) {
93106 fit_expr <- sub_arg_values(object $ method $ fit , opts [" data" ])
94107 fit_expr $ formula <- as.formula(eval(formula ))
95- res <- eval_tidy (fit_expr )
108+ res <- eval_mod (fit_expr , capture = .control $ verbosity == 0 , catch = .control $ catch )
96109 } else {
97110 if (object $ method $ interface %in% c(" data.frame" , " matrix" )) {
98- res <- formula_to_xy(object = object , formula = formula , data = opts [" data" ])
111+ res <- formula_to_xy(object = object , formula = formula , data = opts [" data" ], .control )
99112 } else {
100113 stop(" I don't know about the " ,
101114 object $ method $ interface , " interface." ,
@@ -105,25 +118,24 @@ fit_formula <- function(object, formula, engine = engine, ...) {
105118 res
106119}
107120
108- fit_xy <- function (object , x , ... ) {
121+ fit_xy <- function (object , x , .control , . .. ) {
109122 opts <- quos(... )
110123
111124 if (! any(names(opts ) == " y" ))
112125 stop(" Please pass a data frame with the `y` argument." ,
113126 call. = FALSE )
114127
115128 # TODO Should probably just load the namespace
116- for (pkg in object $ method $ library )
117- suppressPackageStartupMessages(library(pkg , character.only = TRUE ))
129+ load_libs(object , .control $ verbosity < 2 )
118130
119131 # Look up the model's interface (e.g. formula, recipes, etc)
120132 # and delegate to the connector functions (`xy_to_formula` etc)
121133 if (object $ method $ interface == " formula" ) {
122- res <- xy_to_formula(object = object , x = x , y = opts [" y" ])
134+ res <- xy_to_formula(object = object , x = x , y = opts [" y" ], .control )
123135 } else {
124136 if (object $ method $ interface %in% c(" data.frame" , " matrix" )) {
125137 fit_expr <- sub_arg_values(object $ method $ fit , opts [" y" ])
126- res <- eval_tidy (fit_expr )
138+ res <- eval_mod (fit_expr , capture = .control $ verbosity == 0 , catch = .control $ catch )
127139 } else {
128140 stop(" I don't know about the " ,
129141 object $ method $ interface , " interface." ,
@@ -133,24 +145,23 @@ fit_xy <- function(object, x, ...) {
133145 res
134146}
135147
136- fit_recipe <- function (object , recipe , ... ) {
148+ fit_recipe <- function (object , recipe , .control , . .. ) {
137149 opts <- quos(... )
138150
139151 if (! any(names(opts ) == " data" ))
140152 stop(" Please pass a data frame with the `data` argument." ,
141153 call. = FALSE )
142154
143155 # TODO Should probably just load the namespace
144- for (pkg in object $ method $ library )
145- suppressPackageStartupMessages(library(pkg , character.only = TRUE ))
156+ load_libs(object , .control $ verbosity < 2 )
146157
147158 # Look up the model's interface (e.g. formula, recipes, etc)
148159 # and delegate to the connector functions (`recipe_to_formula` etc)
149160 if (object $ method $ interface == " formula" ) {
150- res <- recipe_to_formula(object = object , recipe = recipe , data = opts [" data" ])
161+ res <- recipe_to_formula(object = object , recipe = recipe , data = opts [" data" ], .control )
151162 } else {
152163 if (object $ method $ interface %in% c(" data.frame" , " matrix" )) {
153- res <- recipe_to_xy(object = object , recipe = recipe , data = opts [" data" ])
164+ res <- recipe_to_xy(object = object , recipe = recipe , data = opts [" data" ], .control )
154165 } else {
155166 stop(" I don't know about the " ,
156167 object $ method $ interface , " interface." ,
@@ -163,15 +174,15 @@ fit_recipe <- function(object, recipe, ...) {
163174
164175# ##################################################################
165176
166- formula_to_recipe <- function (object , formula , data ) {
177+ formula_to_recipe <- function (object , formula , data , .control ) {
167178 # execute the formula
168179 # extract terms _and roles_
169180 # put into recipe
170181
171182}
172183
173- # ' @importFrom stats model.frame model.response
174- formula_to_xy <- function (object , formula , data ) {
184+ # ' @importFrom stats model.frame model.response terms
185+ formula_to_xy <- function (object , formula , data , .control ) {
175186 # TODO how do we fill in the other standard things here (subset, contrasts etc)?
176187 # TODO add a "matrix" option here and invoke model.matrix
177188
@@ -182,13 +193,13 @@ formula_to_xy <- function(object, formula, data) {
182193 if (! isTRUE(all.equal(outcome_cols , 0 ))) {
183194 x <- x [,- outcome_cols , drop = FALSE ]
184195 }
185- eval_tidy (object $ method $ fit )
196+ eval_mod (object $ method $ fit , capture = .control $ verbosity == 0 , catch = .control $ catch )
186197}
187198
188199# ##################################################################
189200
190201# ' @importFrom recipes prep juice all_predictors all_outcomes
191- recipe_to_formula <- function (object , recipe , data ) {
202+ recipe_to_formula <- function (object , recipe , data , .control ) {
192203 # TODO case weights
193204 recipe <-
194205 prep(recipe , training = eval_tidy(data [[" data" ]]), retain = TRUE )
@@ -204,10 +215,10 @@ recipe_to_formula <- function(object, recipe, data) {
204215 fit_expr <- object $ method $ fit
205216 fit_expr $ formula <- as.formula(paste0(y_names , " ~." ))
206217 fit_expr $ data <- quote(dat )
207- eval_tidy (fit_expr )
218+ eval_mod (fit_expr , capture = .control $ verbosity == 0 , catch = .control $ catch )
208219}
209220
210- recipe_to_xy <- function (object , recipe , data ) {
221+ recipe_to_xy <- function (object , recipe , data , .control ) {
211222 # TODO case weights
212223 recipe <-
213224 prep(recipe , training = eval_tidy(data [[" data" ]]), retain = TRUE )
@@ -221,12 +232,12 @@ recipe_to_xy <- function(object, recipe, data) {
221232 y <- y [[1 ]]
222233
223234 fit_expr <- object $ method $ fit
224- eval_tidy (fit_expr )
235+ eval_mod (fit_expr , capture = .control $ verbosity == 0 , catch = .control $ catch )
225236}
226237
227238# ##################################################################
228239
229- xy_to_formula <- function (object , x , y ) {
240+ xy_to_formula <- function (object , x , y , .control ) {
230241 if (! is.data.frame(x ))
231242 x <- as.data.frame(x )
232243 x $ .y <- eval_tidy(y [[" y" ]])
@@ -236,6 +247,44 @@ xy_to_formula <- function(object, x, y) {
236247 eval_tidy(fit_expr )
237248}
238249
239- xy_to_recipe <- function (object , x , y ) {
250+ xy_to_recipe <- function (object , x , y , .control ) {
240251
241252}
253+
254+ # ##################################################################
255+
256+ # ' @importFrom utils capture.output
257+ eval_mod <- function (e , capture = FALSE , catch = FALSE ) {
258+ if (capture ) {
259+ if (catch ) {
260+ junk <- capture.output(res <- try(eval_tidy(e ), silent = TRUE ))
261+ } else {
262+ junk <- capture.output(res <- eval_tidy(e ))
263+ }
264+ } else {
265+ if (catch ) {
266+ res <- try(eval_tidy(e ), silent = TRUE )
267+ } else {
268+ res <- eval_tidy(e )
269+ }
270+ }
271+ res
272+ }
273+
274+ # ##################################################################
275+
276+
277+ check_control <- function (x ) {
278+ if (! is.list(x ))
279+ stop(" .control should be a named list." , call. = FALSE )
280+ if (! isTRUE(all.equal(sort(names(x )), c(" catch" , " verbosity" ))))
281+ stop(" .control should be a named list with elements 'verbosity' and 'catch'." ,
282+ call. = FALSE )
283+ # based on ?is.integer
284+ int_check <- function (x , tol = .Machine $ double.eps ^ 0.5 ) abs(x - round(x )) < tol
285+ if (! int_check(x $ verbosity ))
286+ stop(" verbosity should be an integer." , call. = FALSE )
287+ if (! is.logical(x $ catch ))
288+ stop(" catch should be a logical." , call. = FALSE )
289+ x
290+ }
0 commit comments