Skip to content

Commit

Permalink
[R] Change behavior around renamed / removed arguments (#11095)
Browse files Browse the repository at this point in the history
  • Loading branch information
david-cortes authored Dec 28, 2024
1 parent 027eb7b commit 198c3e1
Show file tree
Hide file tree
Showing 12 changed files with 264 additions and 58 deletions.
160 changes: 129 additions & 31 deletions R-package/R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -526,32 +526,73 @@ NULL
#' @name a-compatibility-note-for-saveRDS-save
NULL

#' @name xgboost-options
#' @title XGBoost Options
#' @description XGBoost offers an \link[base:options]{option setting} for controlling the behavior
#' of deprecated and removed function arguments.
#'
#' Some of the arguments in functions like [xgb.train()] or [predict.xgb.Booster()] been renamed
#' from how they were in previous versions, or have been removed.
#'
#' In order to make the transition to newer XGBoost versions easier, some of these parameters are
#' still accepted but issue a warning when using them. \bold{Note that these warnings will become
#' errors in the future!!} - this is just a temporary workaround to make the transition easier.
#'
#' One can optionally use 'strict mode' to turn these warnings into errors, in order to ensure
#' that code calling xgboost will still work once those are removed in future releases.
#'
#' Currently, the only supported option is `xgboost.strict_mode`, which can be set to `TRUE` or
#' `FALSE` (default).
#' @examples
#' options("xgboost.strict_mode" = FALSE)
#' options("xgboost.strict_mode" = TRUE)
NULL

# Lookup table for the deprecated parameters bookkeeping
deprecated_train_params <- list(
'print.every.n' = 'print_every_n',
'early.stop.round' = 'early_stopping_rounds',
'training.data' = 'data',
'dtrain' = 'data',
'watchlist' = 'evals',
'feval' = 'custom_metric'
renamed = list(
'print.every.n' = 'print_every_n',
'early.stop.round' = 'early_stopping_rounds',
'training.data' = 'data',
'dtrain' = 'data',
'watchlist' = 'evals',
'feval' = 'custom_metric'
),
removed = character()
)
deprecated_dttree_params <- list(
'n_first_tree' = 'trees'
renamed = list('n_first_tree' = 'trees'),
removed = c("feature_names", "text")
)
deprecated_plot_params <- list(
'plot.height' = 'plot_height',
'plot.width' = 'plot_width'
deprecated_plotimp_params <- list(
renamed = list(
'plot.height' = 'plot_height',
'plot.width' = 'plot_width'
),
removed = character()
)
deprecated_multitrees_params <- c(
deprecated_plot_params,
list('features.keep' = 'features_keep')
deprecated_multitrees_params <- list(
renamed = c(
deprecated_plotimp_params$renamed,
list('features.keep' = 'features_keep')
),
removed = "feature_names"
)
deprecated_dump_params <- list(
'with.stats' = 'with_stats'
renamed = list('with.stats' = 'with_stats'),
removed = character()
)
deprecated_plottree_params <- c(
deprecated_plot_params,
deprecated_dump_params
renamed = list(
deprecated_plotimp_params$renamed,
deprecated_dump_params$renamed,
list('trees' = 'tree_idx')
),
removed = c("show_node_id", "feature_names")
)
deprecated_predict_params <- list(
renamed = list("ntreelimit" = "iterationrange"),
removed = "reshape"
)

# Checks the dot-parameters for deprecated names
Expand All @@ -569,42 +610,99 @@ check.deprecation <- function(
if (length(params) == 0) {
return(NULL)
}
error_on_deprecated <- getOption("xgboost.strict_mode", default = FALSE)
throw_err_or_depr_msg <- function(...) {
if (error_on_deprecated) {
stop(...)
} else {
warning(..., " This warning will become an error in a future version.")
}
}

if (is.null(names(params)) || min(nchar(names(params))) == 0L) {
stop("Passed invalid positional arguments")
throw_err_or_depr_msg("Passed invalid positional arguments")
}
all_match <- pmatch(names(params), names(deprecated_list))
list_renamed <- deprecated_list$renamed
list_removed <- deprecated_list$removed
has_params_arg <- list_renamed[[1L]] == deprecated_train_params$renamed[[1L]]
all_match <- pmatch(names(params), names(list_renamed))
# throw error on unrecognized parameters
if (!allow_unrecognized && anyNA(all_match)) {

names_unrecognized <- names(params)[is.na(all_match)]
# make it informative if they match something that goes under 'params'
if (deprecated_list[[1L]] == deprecated_train_params[[1L]]) {
if (has_params_arg) {
names_params <- formalArgs(xgb.params)
names_params <- c(names_params, gsub("_", ".", names_params, fixed = TRUE))
names_under_params <- intersect(names_unrecognized, names_params)
if (length(names_under_params)) {
stop(
"Passed invalid function arguments: ",
paste(head(names_under_params), collapse = ", "),
". These should be passed as a list to argument 'params'."
)
if (error_on_deprecated) {
stop(
"Passed invalid function arguments: ",
paste(head(names_under_params), collapse = ", "),
". These should be passed as a list to argument 'params'."
)
} else {
warning(
"Passed invalid function arguments: ",
paste(head(names_under_params), collapse = ", "),
". These should be passed as a list to argument 'params'.",
" Conversion from argument to 'params' entry will be done automatically, but this ",
"behavior will become an error in a future version."
)
if (any(names_under_params %in% names(env[["params"]]))) {
repeteated_params <- intersect(names_under_params, names(env[["params"]]))
stop(
"Passed entries as both function argument(s) and as elements under 'params': ",
paste(head(repeteated_params), collapse = ", ")
)
} else {
env[["params"]] <- c(env[["params"]], params[names_under_params])
}
}
names_unrecognized <- setdiff(names_unrecognized, names_under_params)
}
}

# check for parameters that were removed from a previous version
names_removed <- intersect(names_unrecognized, list_removed)
if (length(names_removed)) {
throw_err_or_depr_msg(
"Parameter(s) have been removed from this function: ",
paste(names_removed, collapse = ", "), "."
)
names_unrecognized <- setdiff(names_unrecognized, list_removed)
}

# otherwise throw a generic error
stop(
"Passed unrecognized parameters: ",
paste(head(names_unrecognized), collapse = ", ")
)
if (length(names_unrecognized)) {
throw_err_or_depr_msg(
"Passed unrecognized parameters: ",
paste(head(names_unrecognized), collapse = ", ")
)
}

} else {

names_removed <- intersect(names(params)[is.na(all_match)], list_removed)
if (length(names_removed)) {
throw_err_or_depr_msg(
"Parameter(s) have been removed from this function: ",
paste(names_removed, collapse = ", "), "."
)
}

}

matched_params <- deprecated_list[all_match[!is.na(all_match)]]
matched_params <- list_renamed[all_match[!is.na(all_match)]]
idx_orig <- seq_along(params)[!is.na(all_match)]
function_args_passed <- names(as.list(fn_call))[-1L]
for (idx in seq_along(matched_params)) {
match_old <- names(matched_params)[[idx]]
match_new <- matched_params[[idx]]
warning(
throw_err_or_depr_msg(
"Parameter '", match_old, "' has been renamed to '",
match_new, "' and will be removed in a future version."
match_new, "'."
)
if (match_new %in% function_args_passed) {
stop("Passed both '", match_new, "' and '", match_old, "'.")
Expand Down
4 changes: 1 addition & 3 deletions R-package/R/xgb.Booster.R
Original file line number Diff line number Diff line change
Expand Up @@ -354,9 +354,7 @@ predict.xgb.Booster <- function(object, newdata, missing = NA, outputmargin = FA
predleaf = FALSE, predcontrib = FALSE, approxcontrib = FALSE, predinteraction = FALSE,
training = FALSE, iterationrange = NULL, strict_shape = FALSE, avoid_transpose = FALSE,
validate_features = FALSE, base_margin = NULL, ...) {
if (NROW(list(...))) {
warning("Passed unused prediction arguments: ", paste(names(list(...)), collapse = ", "), ".")
}
check.deprecation(deprecated_predict_params, match.call(), ..., allow_unrecognized = TRUE)
if (validate_features) {
newdata <- validate.features(object, newdata)
}
Expand Down
2 changes: 1 addition & 1 deletion R-package/R/xgb.plot.importance.R
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
#' @export
xgb.plot.importance <- function(importance_matrix = NULL, top_n = NULL, measure = NULL,
rel_to_first = FALSE, left_margin = 10, cex = NULL, plot = TRUE, ...) {
check.deprecation(deprecated_plot_params, match.call(), ..., allow_unrecognized = TRUE)
check.deprecation(deprecated_plotimp_params, match.call(), ..., allow_unrecognized = TRUE)
if (!is.data.table(importance_matrix)) {
stop("importance_matrix: must be a data.table")
}
Expand Down
13 changes: 10 additions & 3 deletions R-package/R/xgb.train.R
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,18 @@
#' [xgb.save()] (but are kept when using R serializers like [saveRDS()]).
#' @param ... Not used.
#'
#' Some arguments are currently deprecated or have been renamed. If a deprecated argument
#' is passed, will throw a warning and use its current equivalent.
#' Some arguments that were part of this function in previous XGBoost versions are currently
#' deprecated or have been renamed. If a deprecated or renamed argument is passed, will throw
#' a warning (by default) and use its current equivalent instead. This warning will become an
#' error if using the \link[=xgboost-options]{'strict mode' option}.
#'
#' If some additional argument is passed that is neither a current function argument nor
#' a deprecated argument, an error will be thrown.
#' a deprecated or renamed argument, a warning or error will be thrown depending on the
#' 'strict mode' option.
#'
#' \bold{Important:} `...` will be removed in a future version, and all the current
#' deprecation warnings will become errors. Please use only arguments that form part of
#' the function signature.
#' @return An object of class `xgb.Booster`.
#' @details
#' Compared to [xgboost()], the `xgb.train()` interface supports advanced features such as
Expand Down
13 changes: 10 additions & 3 deletions R-package/man/xgb.cv.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 10 additions & 3 deletions R-package/man/xgb.dump.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 10 additions & 3 deletions R-package/man/xgb.model.dt.tree.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 10 additions & 3 deletions R-package/man/xgb.plot.multi.trees.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 10 additions & 3 deletions R-package/man/xgb.plot.tree.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 10 additions & 3 deletions R-package/man/xgb.train.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 198c3e1

Please sign in to comment.