Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

workshop error messages #167

Merged
merged 9 commits into from Dec 12, 2022
4 changes: 4 additions & 0 deletions NEWS.md
Expand Up @@ -23,6 +23,10 @@ To be released as stacks 1.0.1.
a [new article](https://stacks.tidymodels.org/dev/articles/workflowsets.html)
on the package website.

* Revamped errors, warnings, and messages. Prompts now provide more thorough
context about where they arose, include more extensive references to
documentation, and are correctly pluralized.

# stacks 1.0.0

stacks 1.0.0 is the first production release of the package. While this release
Expand Down
52 changes: 24 additions & 28 deletions R/add_candidates.R
Expand Up @@ -118,7 +118,7 @@ add_candidates.workflow_set <- function(data_stack, candidates,
} else {
cli_abort(
"The supplied workflow set must be fitted to resamples with
`workflows::workflow_map()` before being added to a data stack.",
{.help [`workflow_map()`](workflowsets::workflow_map)} before being added to a data stack.",
call = caller_env(0),
class = "wf_set_unfitted"
)
Expand Down Expand Up @@ -158,9 +158,10 @@ add_candidates.default <- function(data_stack, candidates, name, ...) {
check_add_data_stack(data_stack)

cli_abort(
"The second argument to add_candidates() should inherit from one of
`tune_results` or `workflow_set`, but its class
is {list(class(candidates))}.",
"The second argument to {.help [`add_candidates()`](stacks::add_candidates)} should inherit from one of
{.help [`tune_results`](tune::tune_grid)} or
{.help [`workflow_set`](workflowsets::workflow_set)}, but its class
is {.var {class(candidates)}}.",
call = caller_env(0)
)
}
Expand All @@ -169,8 +170,8 @@ add_candidates.default <- function(data_stack, candidates, name, ...) {
if (!.get_outcome(stack) %in% c("init_", tune::.get_tune_outcome_names(candidates))) {
cli_abort(
"The model definition you've tried to add to the stack has
outcome variable {list(tune::.get_tune_outcome_names(candidates))},
while the stack's outcome variable is {.get_outcome(stack)}.",
outcome variable {.var {tune::.get_tune_outcome_names(candidates)}},
while the stack's outcome variable is {.var {.get_outcome(stack)}}.",
call = caller_env(1)
)
}
Expand Down Expand Up @@ -253,7 +254,7 @@ add_candidates.default <- function(data_stack, candidates, name, ...) {
cli_abort(
"The supplied candidates were tuned/fitted using only metrics that
rely on hard class predictions. Please tune/fit with at least one
class probability-based metric, such as `yardstick::roc_auc()`.",
class probability-based metric, such as {.help [`roc_auc`](yardstick::roc_auc)}.",
call = caller_env(1)
)
}
Expand Down Expand Up @@ -363,14 +364,8 @@ rm_duplicate_cols <- function(df) {
exclude <- c(exclude, names(df[duplicated(purrr::map(df, c))]))

if (length(exclude) > 0) {
if (length(exclude) > 1) {
n_candidates <- paste(length(exclude), "candidates")
} else {
n_candidates <- "1 candidate"
}

cli_warn(
"Predictions from {n_candidates} were identical to
"Predictions from {length(exclude)} candidate{?s} were identical to
those from existing candidates and were removed from the data stack."
)

Expand Down Expand Up @@ -428,32 +423,33 @@ check_add_data_stack <- function(data_stack) {
c("tune_results", "tune_bayes", "resample_results")
)) {
cli_abort(
"It looks like the first argument inherits from {list(class(data_stack))}
rather than `data_stack`.
"It looks like the first argument inherits from {.var {class(data_stack)}}
rather than {.var data_stack}.
Did you accidentally supply the candidate members as the first argument?
If so, please supply the output of `stacks()` or another `add_candidates()` as
the argument to `data_stack`.",
If so, please supply the output of {.help [`stacks()`](stacks::stacks)} or another
{.help [`add_candidates()`](stacks::add_candidates)} call as
the argument to {.arg data_stack}.",
call = caller_env()
)
} else {
check_inherits(data_stack, "data_stack")
check_inherits(data_stack, "data_stack", call = caller_env())
}
}

check_candidates <- function(candidates, name) {
if (nrow(tune::collect_notes(candidates)) != 0) {
cli_warn(
"The inputted `candidates` argument `{name}` generated notes during
"The inputted {.arg candidates} argument {.var {name}} generated notes during
tuning/resampling. Model stacking may fail due to these
issues; see `?collect_notes` if so."
issues; see {.help [`collect_notes()`](tune::collect_notes)} if so."
)
}

if ((!".predictions" %in% colnames(candidates)) |
is.null(attributes(candidates)$workflow)) {
cli_abort(
"The inputted `candidates` argument was not generated with the
appropriate control settings. Please see ?control_stack.",
"The inputted {.arg candidates} argument was not generated with the
appropriate control settings. Please see {.help [`control_stack()`](stacks::control_stack)}.",
call = caller_env()
)
}
Expand All @@ -465,17 +461,17 @@ check_name <- function(name) {
c("tune_results", "tune_bayes", "resample_results")
)) {
cli_abort(
"The inputted `name` argument looks like a tuning/fitting results object
that might be supplied as a `candidates` argument. Did you try to add
more than one set of candidates in one `add_candidates()` call?",
"The inputted {.arg name} argument looks like a tuning/fitting results object
that might be supplied as a {.arg candidates} argument. Did you try to add
more than one set of candidates in one {.help [`add_candidates()`](stacks::add_candidates)} call?",
call = caller_env()
)
} else {
check_inherits(name, "character")
check_inherits(name, "character", call = caller_env())

if (make.names(name) != name) {
cli_inform(
"The inputted `name` argument cannot prefix a valid column name. The
"The inputted {.arg name} argument cannot prefix a valid column name. The
data stack will use '{make.names(name)}' rather than '{name}' in
constructing candidate names."
)
Expand Down
12 changes: 7 additions & 5 deletions R/blend_predictions.R
Expand Up @@ -247,7 +247,7 @@ check_regularization <- function(x, arg) {
if (!is.numeric(x)) {
cli_abort(
"The argument to '{arg}' must be a numeric, but the supplied {arg}'s
class is `{list(class(x))}`",
class is {.var {class(x)}}.",
call = caller_env()
)
}
Expand Down Expand Up @@ -337,19 +337,20 @@ check_blend_data_stack <- function(data_stack) {
# carry out in fit_members() -- just check for bare stacks, 1-candidate
# stacks, and non-stack objects
if (!inherits(data_stack, "data_stack")) {
check_inherits(data_stack, "data_stack")
check_inherits(data_stack, "data_stack", call = caller_env())
} else if (ncol(data_stack) == 0) {
cli_abort(
"The data stack supplied as the argument to `data_stack` has no
candidate members. Please first add candidates with
the `add_candidates()` function.",
the {.help [`add_candidates()`](stacks::add_candidates)} function.",
call = caller_env()
)
} else if ((ncol(data_stack) == 2 && attr(data_stack, "mode") == "regression") ||
ncol(data_stack) == length(levels(data_stack[[1]])) + 1) {
cli_abort(
"The supplied data stack only contains one candidate member. Please
add more candidate members using `add_candidates()` before blending.",
add more candidate members using
{.help [`add_candidates()`](stacks::add_candidates)} before blending.",
call = caller_env()
)
}
Expand All @@ -371,7 +372,8 @@ process_data_stack <- function(data_stack) {
if (nrow(dat) < nrow(data_stack)) {
cli_inform(
"{nrow(data_stack) - nrow(dat)} of the {nrow(data_stack)} rows in the
data stack have missing values, and will be omitted in the blending process."
data stack {cli::qty({nrow(data_stack) - nrow(dat)})} {?has/have} missing
values, and will be omitted in the blending process."
)
}

Expand Down
2 changes: 1 addition & 1 deletion R/collect_parameters.R
Expand Up @@ -52,7 +52,7 @@ collect_parameters <- function(stack, candidates, ...) {
collect_parameters.default <- function(stack, candidates, ...) {
cli_abort(
"There is no `collect_parameters()` method currently implemented
for {list(class(stack))} objects.",
for {.var {class(stack)}} objects.",
call = caller_env(0)
)
}
Expand Down
24 changes: 8 additions & 16 deletions R/fit_members.R
Expand Up @@ -224,21 +224,22 @@ check_model_stack <- function(model_stack) {
if (inherits(model_stack, "model_stack")) {
if (!is.null(model_stack[["member_fits"]])) {
cli_warn(
"The members in the supplied `model_stack` have already been fitted
"The members in the supplied {.arg model_stack} have already been fitted
and need not be fitted again."
)
}

return(invisible(TRUE))
} else if (inherits(model_stack, "data_stack")) {
cli_abort(
"The supplied `model_stack` argument is a data stack rather than
"The supplied {.arg model_stack} argument is a data stack rather than
a model stack. Did you forget to first evaluate the ensemble's
stacking coefficients with `blend_predictions()`?",
stacking coefficients with
{.help [`blend_predictions()`](stacks::blend_predictions)}?",
call = caller_env()
)
} else {
check_inherits(model_stack, "model_stack")
check_inherits(model_stack, "model_stack", call = caller_env())
}
}

Expand Down Expand Up @@ -277,20 +278,11 @@ check_for_required_packages <- function(x) {
# takes in a vector of package names and a logical vector giving
# whether or not each is installed
error_needs_install <- function(pkgs, installed, call) {
plural <- sum(!installed) != 1

last_sep <- if (sum(!installed) == 2) {"` and `"} else {"`, and `"}

need_install <- paste0(
"`",
glue::glue_collapse(pkgs[!installed], sep = "`, `", last = last_sep),
"`"
)
need_install <- pkgs[!installed]

cli_abort(
"The following package{if (plural) 's' else ''}
need{if (plural) '' else 's'} to be installed before
fitting members: {need_install}",
"{cli::qty(need_install)}The package{?s} {.pkg {need_install}} need{?s/} to be
installed before fitting members.",
call = call
)
}
Expand Down
8 changes: 4 additions & 4 deletions R/utils.R
Expand Up @@ -80,14 +80,14 @@ check_empty_ellipses <- function(...) {
invisible(NULL)
}

check_inherits <- function(x, what) {
check_inherits <- function(x, what, call = caller_env()) {
cl <- match.call()

if (!inherits(x, what)) {
cli_abort(
"Element `{list(cl$x)}` needs to inherit from `{what}`, but its
class is `{list(class(x))}`.",
call = NULL
"Element {.val {cl$x}} needs to inherit from {.var {what}}, but its
class is {.var {class(x)}}.",
call = call
)
}

Expand Down