From 280aab0319d3974b8802199428d1755a1116ea23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=98topepo=E2=80=99?= <‘mxkuhn@gmail.com’> Date: Wed, 3 Apr 2024 16:26:34 -0400 Subject: [PATCH 01/10] add a function to constrain numeric predictions --- DESCRIPTION | 2 +- NAMESPACE | 1 + NEWS.md | 2 ++ R/bound_prediction.R | 33 +++++++++++++++++++++++++++++++++ man/bound_prediction.Rd | 27 +++++++++++++++++++++++++++ man/probably-package.Rd | 4 +++- 6 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 R/bound_prediction.R create mode 100644 man/bound_prediction.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 3f81696a..82b2437a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -59,4 +59,4 @@ Config/testthat/edition: 3 Encoding: UTF-8 LazyData: true Roxygen: list(markdown = TRUE) -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.1 diff --git a/NAMESPACE b/NAMESPACE index 9e678da7..501604f0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -127,6 +127,7 @@ export(as.factor) export(as.ordered) export(as_class_pred) export(augment) +export(bound_prediction) export(cal_apply) export(cal_estimate_beta) export(cal_estimate_isotonic) diff --git a/NEWS.md b/NEWS.md index 9596665a..043eda1e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # probably (development version) +* A new function `bound_prediction()` is available to constrain the values of a numeric prediction. + # probably 1.0.3 * Fixed a bug where the grouping for calibration methods was sensitive to the type of the grouping variables (#127). diff --git a/R/bound_prediction.R b/R/bound_prediction.R new file mode 100644 index 00000000..f276548a --- /dev/null +++ b/R/bound_prediction.R @@ -0,0 +1,33 @@ +#' Truncate a numeric prediction column +#' +#' For user-defined lower and/or upper bound, ensure that the values in the +#' `.pred` column are coerced to these bounds. +#' +#' @param x A data frame that contains a numeric column named `.pred`. +#' @param lower,upper Single numerics (or `NA`) that define constrains on `.pred`. +#' @return `x` with potentially adjusted values. +#' @examples +#' data(solubility_test, package = "yardstick") +#' +#' names(solubility_test) <- c("solubility", ".pred") +#' +#' bound_prediction(solubility_test, lower = -1) +#' @export +bound_prediction <- function(x, lower = -Inf, upper = Inf) { + if (!any(names(x) == ".pred")) { + cli::cli_abort("The argument {.arg x} should have a column named {.code .pred}") + } + if (!is.numeric(x$.pred)) { + cli::cli_abort("Column {.code .pred} should be numeric.") + } + + if (is.numeric(lower) & !is.na(lower)) { + x$.pred <- ifelse(x$.pred < lower, lower, x$.pred) + } + + if (is.numeric(upper) & !is.na(upper)) { + x$.pred <- ifelse(x$.pred > upper, upper, x$.pred) + } + x +} + diff --git a/man/bound_prediction.Rd b/man/bound_prediction.Rd new file mode 100644 index 00000000..394d6353 --- /dev/null +++ b/man/bound_prediction.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/bound_prediction.R +\name{bound_prediction} +\alias{bound_prediction} +\title{Truncate a numeric prediction column} +\usage{ +bound_prediction(x, lower = -Inf, upper = Inf) +} +\arguments{ +\item{x}{A data frame that contains a numeric column named \code{.pred}.} + +\item{lower, upper}{Single numerics (or \code{NA}) that define constrains on \code{.pred}.} +} +\value{ +\code{x} with potentially adjusted values. +} +\description{ +For user-defined lower and/or upper bound, ensure that the values in the +\code{.pred} column are coerced to these bounds. +} +\examples{ +data(solubility_test, package = "yardstick") + +names(solubility_test) <- c("solubility", ".pred") + +bound_prediction(solubility_test, lower = -1) +} diff --git a/man/probably-package.Rd b/man/probably-package.Rd index 8d03bbb9..3036d3f2 100644 --- a/man/probably-package.Rd +++ b/man/probably-package.Rd @@ -4,8 +4,10 @@ \name{probably-package} \alias{probably} \alias{probably-package} -\title{probably: Tools for Post-Processing Class Probability Estimates} +\title{probably: Tools for Post-Processing Predicted Values} \description{ +\if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}} + Models can be improved by post-processing class probabilities, by: recalibration, conversion to hard probabilities, assessment of equivocal zones, and other activities. 'probably' contains tools for conducting these operations as well as calibration tools and conformal inference techniques for regression models. } \seealso{ From 5f26a3714e3dac7bd1e09632bcd4854d1ec623a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=98topepo=E2=80=99?= <‘mxkuhn@gmail.com’> Date: Thu, 4 Apr 2024 11:06:38 -0400 Subject: [PATCH 02/10] test cases --- R/bound_prediction.R | 10 +++-- tests/testthat/_snaps/bound-prediction.md | 34 +++++++++++++++++ tests/testthat/test-bound-prediction.R | 45 +++++++++++++++++++++++ 3 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 tests/testthat/_snaps/bound-prediction.md create mode 100644 tests/testthat/test-bound-prediction.R diff --git a/R/bound_prediction.R b/R/bound_prediction.R index f276548a..9e5d94db 100644 --- a/R/bound_prediction.R +++ b/R/bound_prediction.R @@ -5,6 +5,7 @@ #' #' @param x A data frame that contains a numeric column named `.pred`. #' @param lower,upper Single numerics (or `NA`) that define constrains on `.pred`. +#' @param call The call to be displayed in warnings or errors. #' @return `x` with potentially adjusted values. #' @examples #' data(solubility_test, package = "yardstick") @@ -13,12 +14,15 @@ #' #' bound_prediction(solubility_test, lower = -1) #' @export -bound_prediction <- function(x, lower = -Inf, upper = Inf) { +bound_prediction <- function(x, lower = -Inf, upper = Inf, + call = rlang::caller_env()) { if (!any(names(x) == ".pred")) { - cli::cli_abort("The argument {.arg x} should have a column named {.code .pred}") + cli::cli_abort("The argument {.arg x} should have a column named {.code .pred}", + call = call) } if (!is.numeric(x$.pred)) { - cli::cli_abort("Column {.code .pred} should be numeric.") + cli::cli_abort("Column {.code .pred} should be numeric.", + call = call) } if (is.numeric(lower) & !is.na(lower)) { diff --git a/tests/testthat/_snaps/bound-prediction.md b/tests/testthat/_snaps/bound-prediction.md new file mode 100644 index 00000000..a7d56d42 --- /dev/null +++ b/tests/testthat/_snaps/bound-prediction.md @@ -0,0 +1,34 @@ +# lower bounds for numeric predictions + + Code + bound_prediction(solubility_test, lower = 2) + Condition + Error: + ! The argument `x` should have a column named `.pred` + +--- + + Code + solubility_test %>% mutate(.pred = format(prediction)) %>% bound_prediction( + lower = 2) + Condition + Error: + ! Column `.pred` should be numeric. + +# upper bounds for numeric predictions + + Code + bound_prediction(solubility_test, lower = 2) + Condition + Error: + ! The argument `x` should have a column named `.pred` + +--- + + Code + solubility_test %>% mutate(.pred = format(prediction)) %>% bound_prediction( + lower = 2) + Condition + Error: + ! Column `.pred` should be numeric. + diff --git a/tests/testthat/test-bound-prediction.R b/tests/testthat/test-bound-prediction.R new file mode 100644 index 00000000..df4c75dc --- /dev/null +++ b/tests/testthat/test-bound-prediction.R @@ -0,0 +1,45 @@ +test_that("lower bounds for numeric predictions", { + skip_if_not_installed("modeldata") + library(dplyr) + library(rlang) + data("solubility_test", package = "modeldata") + + expect_snapshot(bound_prediction(solubility_test, lower = 2), error = TRUE) + expect_snapshot( + solubility_test %>% + mutate(.pred = format(prediction)) %>% + bound_prediction(lower = 2), + error = TRUE) + + sol <- solubility_test %>% set_names(c("solubility", ".pred")) + + expect_equal(bound_prediction(sol), sol) + expect_equal(bound_prediction(sol, lower = NA), sol) + + res_1 <- bound_prediction(sol, lower = -1) + expect_true(all(res_1$.pred[res_1$.pred < -1] == -1)) + expect_true(all(res_1$.pred[res_1$.pred >= -1] == res_1$.pred[res_1$.pred >= -1])) +}) + +test_that("upper bounds for numeric predictions", { + skip_if_not_installed("modeldata") + library(dplyr) + library(rlang) + data("solubility_test", package = "modeldata") + + expect_snapshot(bound_prediction(solubility_test, lower = 2), error = TRUE) + expect_snapshot( + solubility_test %>% + mutate(.pred = format(prediction)) %>% + bound_prediction(lower = 2), + error = TRUE) + + sol <- solubility_test %>% set_names(c("solubility", ".pred")) + + expect_equal(bound_prediction(sol), sol) + expect_equal(bound_prediction(sol, upper = NA), sol) + + res_1 <- bound_prediction(sol, upper = -1) + expect_true(all(res_1$.pred[res_1$.pred > -1] == -1)) + expect_true(all(res_1$.pred[res_1$.pred <= -1] == res_1$.pred[res_1$.pred <= -1])) +}) From 5b1a3dfe43c3583e434f17fe48df3d27d987f77a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=98topepo=E2=80=99?= <‘mxkuhn@gmail.com’> Date: Thu, 4 Apr 2024 11:13:30 -0400 Subject: [PATCH 03/10] argument name change --- R/bound_prediction.R | 17 +++++++++-------- _pkgdown.yml | 1 + man/bound_prediction.Rd | 16 ++++++++++++---- tests/testthat/test-bound-prediction.R | 20 ++++++++++---------- 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/R/bound_prediction.R b/R/bound_prediction.R index 9e5d94db..b62bac62 100644 --- a/R/bound_prediction.R +++ b/R/bound_prediction.R @@ -1,10 +1,11 @@ #' Truncate a numeric prediction column #' -#' For user-defined lower and/or upper bound, ensure that the values in the +#' For user-defined lower_limit and/or upper_limit bound, ensure that the values in the #' `.pred` column are coerced to these bounds. #' #' @param x A data frame that contains a numeric column named `.pred`. -#' @param lower,upper Single numerics (or `NA`) that define constrains on `.pred`. +#' @param lower_limit,upper_limit Single numerics (or `NA`) that define +#' constrains on `.pred`. #' @param call The call to be displayed in warnings or errors. #' @return `x` with potentially adjusted values. #' @examples @@ -12,9 +13,9 @@ #' #' names(solubility_test) <- c("solubility", ".pred") #' -#' bound_prediction(solubility_test, lower = -1) +#' bound_prediction(solubility_test, lower_limit = -1) #' @export -bound_prediction <- function(x, lower = -Inf, upper = Inf, +bound_prediction <- function(x, lower_limit = -Inf, upper_limit = Inf, call = rlang::caller_env()) { if (!any(names(x) == ".pred")) { cli::cli_abort("The argument {.arg x} should have a column named {.code .pred}", @@ -25,12 +26,12 @@ bound_prediction <- function(x, lower = -Inf, upper = Inf, call = call) } - if (is.numeric(lower) & !is.na(lower)) { - x$.pred <- ifelse(x$.pred < lower, lower, x$.pred) + if (is.numeric(lower_limit) & !is.na(lower_limit)) { + x$.pred <- ifelse(x$.pred < lower_limit, lower_limit, x$.pred) } - if (is.numeric(upper) & !is.na(upper)) { - x$.pred <- ifelse(x$.pred > upper, upper, x$.pred) + if (is.numeric(upper_limit) & !is.na(upper_limit)) { + x$.pred <- ifelse(x$.pred > upper_limit, upper_limit, x$.pred) } x } diff --git a/_pkgdown.yml b/_pkgdown.yml index 374d4dc3..46586a34 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -37,6 +37,7 @@ reference: - starts_with("int_") - starts_with("control_conformal_") - starts_with("predict.int") + - bound_prediction - title: Data contents: diff --git a/man/bound_prediction.Rd b/man/bound_prediction.Rd index 394d6353..f24b4819 100644 --- a/man/bound_prediction.Rd +++ b/man/bound_prediction.Rd @@ -4,18 +4,26 @@ \alias{bound_prediction} \title{Truncate a numeric prediction column} \usage{ -bound_prediction(x, lower = -Inf, upper = Inf) +bound_prediction( + x, + lower_limit = -Inf, + upper_limit = Inf, + call = rlang::caller_env() +) } \arguments{ \item{x}{A data frame that contains a numeric column named \code{.pred}.} -\item{lower, upper}{Single numerics (or \code{NA}) that define constrains on \code{.pred}.} +\item{lower_limit, upper_limit}{Single numerics (or \code{NA}) that define +constrains on \code{.pred}.} + +\item{call}{The call to be displayed in warnings or errors.} } \value{ \code{x} with potentially adjusted values. } \description{ -For user-defined lower and/or upper bound, ensure that the values in the +For user-defined lower_limit and/or upper_limit bound, ensure that the values in the \code{.pred} column are coerced to these bounds. } \examples{ @@ -23,5 +31,5 @@ data(solubility_test, package = "yardstick") names(solubility_test) <- c("solubility", ".pred") -bound_prediction(solubility_test, lower = -1) +bound_prediction(solubility_test, lower_limit = -1) } diff --git a/tests/testthat/test-bound-prediction.R b/tests/testthat/test-bound-prediction.R index df4c75dc..82184e34 100644 --- a/tests/testthat/test-bound-prediction.R +++ b/tests/testthat/test-bound-prediction.R @@ -1,45 +1,45 @@ -test_that("lower bounds for numeric predictions", { +test_that("lower_limit bounds for numeric predictions", { skip_if_not_installed("modeldata") library(dplyr) library(rlang) data("solubility_test", package = "modeldata") - expect_snapshot(bound_prediction(solubility_test, lower = 2), error = TRUE) + expect_snapshot(bound_prediction(solubility_test, lower_limit = 2), error = TRUE) expect_snapshot( solubility_test %>% mutate(.pred = format(prediction)) %>% - bound_prediction(lower = 2), + bound_prediction(lower_limit = 2), error = TRUE) sol <- solubility_test %>% set_names(c("solubility", ".pred")) expect_equal(bound_prediction(sol), sol) - expect_equal(bound_prediction(sol, lower = NA), sol) + expect_equal(bound_prediction(sol, lower_limit = NA), sol) - res_1 <- bound_prediction(sol, lower = -1) + res_1 <- bound_prediction(sol, lower_limit = -1) expect_true(all(res_1$.pred[res_1$.pred < -1] == -1)) expect_true(all(res_1$.pred[res_1$.pred >= -1] == res_1$.pred[res_1$.pred >= -1])) }) -test_that("upper bounds for numeric predictions", { +test_that("upper_limit bounds for numeric predictions", { skip_if_not_installed("modeldata") library(dplyr) library(rlang) data("solubility_test", package = "modeldata") - expect_snapshot(bound_prediction(solubility_test, lower = 2), error = TRUE) + expect_snapshot(bound_prediction(solubility_test, lower_limit = 2), error = TRUE) expect_snapshot( solubility_test %>% mutate(.pred = format(prediction)) %>% - bound_prediction(lower = 2), + bound_prediction(lower_limit = 2), error = TRUE) sol <- solubility_test %>% set_names(c("solubility", ".pred")) expect_equal(bound_prediction(sol), sol) - expect_equal(bound_prediction(sol, upper = NA), sol) + expect_equal(bound_prediction(sol, upper_limit = NA), sol) - res_1 <- bound_prediction(sol, upper = -1) + res_1 <- bound_prediction(sol, upper_limit = -1) expect_true(all(res_1$.pred[res_1$.pred > -1] == -1)) expect_true(all(res_1$.pred[res_1$.pred <= -1] == res_1$.pred[res_1$.pred <= -1])) }) From f8ab0f81a119bfdf5557488ef218ded8f2d4b34b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=98topepo=E2=80=99?= <‘mxkuhn@gmail.com’> Date: Thu, 4 Apr 2024 11:20:02 -0400 Subject: [PATCH 04/10] test against tune() value --- R/bound_prediction.R | 4 ++-- tests/testthat/Rplots.pdf | Bin 0 -> 54039 bytes tests/testthat/_snaps/bound-prediction.md | 12 ++++++------ tests/testthat/test-bound-prediction.R | 10 ++++++++++ 4 files changed, 18 insertions(+), 8 deletions(-) create mode 100644 tests/testthat/Rplots.pdf diff --git a/R/bound_prediction.R b/R/bound_prediction.R index b62bac62..a81a4481 100644 --- a/R/bound_prediction.R +++ b/R/bound_prediction.R @@ -26,11 +26,11 @@ bound_prediction <- function(x, lower_limit = -Inf, upper_limit = Inf, call = call) } - if (is.numeric(lower_limit) & !is.na(lower_limit)) { + if (is.numeric(lower_limit) && !is.na(lower_limit)) { x$.pred <- ifelse(x$.pred < lower_limit, lower_limit, x$.pred) } - if (is.numeric(upper_limit) & !is.na(upper_limit)) { + if (is.numeric(upper_limit) && !is.na(upper_limit)) { x$.pred <- ifelse(x$.pred > upper_limit, upper_limit, x$.pred) } x diff --git a/tests/testthat/Rplots.pdf b/tests/testthat/Rplots.pdf new file mode 100644 index 0000000000000000000000000000000000000000..14a622dba84b05767be9fe5d78d7cd237622c6ed GIT binary patch literal 54039 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

>kqeB#PwyQEs&~hj`Wf^d0s6 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 mutate(.pred = format(prediction)) %>% bound_prediction( - lower = 2) + lower_limit = 2) Condition Error: ! Column `.pred` should be numeric. -# upper bounds for numeric predictions +# upper_limit bounds for numeric predictions Code - bound_prediction(solubility_test, lower = 2) + bound_prediction(solubility_test, lower_limit = 2) Condition Error: ! The argument `x` should have a column named `.pred` @@ -27,7 +27,7 @@ Code solubility_test %>% mutate(.pred = format(prediction)) %>% bound_prediction( - lower = 2) + lower_limit = 2) Condition Error: ! Column `.pred` should be numeric. diff --git a/tests/testthat/test-bound-prediction.R b/tests/testthat/test-bound-prediction.R index 82184e34..a9fc51c7 100644 --- a/tests/testthat/test-bound-prediction.R +++ b/tests/testthat/test-bound-prediction.R @@ -3,6 +3,9 @@ test_that("lower_limit bounds for numeric predictions", { library(dplyr) library(rlang) data("solubility_test", package = "modeldata") + tune2 <- function() call("tune", "test") + + # ------------------------------------------------------------------------------ expect_snapshot(bound_prediction(solubility_test, lower_limit = 2), error = TRUE) expect_snapshot( @@ -19,6 +22,8 @@ test_that("lower_limit bounds for numeric predictions", { res_1 <- bound_prediction(sol, lower_limit = -1) expect_true(all(res_1$.pred[res_1$.pred < -1] == -1)) expect_true(all(res_1$.pred[res_1$.pred >= -1] == res_1$.pred[res_1$.pred >= -1])) + + expect_equal(bound_prediction(sol, lower_limit = tune2()), sol) }) test_that("upper_limit bounds for numeric predictions", { @@ -26,6 +31,9 @@ test_that("upper_limit bounds for numeric predictions", { library(dplyr) library(rlang) data("solubility_test", package = "modeldata") + tune2 <- function() call("tune", "test") + + # ------------------------------------------------------------------------------ expect_snapshot(bound_prediction(solubility_test, lower_limit = 2), error = TRUE) expect_snapshot( @@ -42,4 +50,6 @@ test_that("upper_limit bounds for numeric predictions", { res_1 <- bound_prediction(sol, upper_limit = -1) expect_true(all(res_1$.pred[res_1$.pred > -1] == -1)) expect_true(all(res_1$.pred[res_1$.pred <= -1] == res_1$.pred[res_1$.pred <= -1])) + + expect_equal(bound_prediction(sol, upper_limit = tune2()), sol) }) From 0b63a0e415151d3368042beafbf49b30404f2bc5 Mon Sep 17 00:00:00 2001 From: Max Kuhn Date: Thu, 4 Apr 2024 14:15:23 -0400 Subject: [PATCH 05/10] Apply suggestions from code review Co-authored-by: Simon P. Couch --- NEWS.md | 2 +- R/bound_prediction.R | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/NEWS.md b/NEWS.md index 043eda1e..6d0382bf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ # probably (development version) -* A new function `bound_prediction()` is available to constrain the values of a numeric prediction. +* A new function `bound_prediction()` is available to constrain the values of a numeric prediction (#142). # probably 1.0.3 diff --git a/R/bound_prediction.R b/R/bound_prediction.R index a81a4481..e1ccfec1 100644 --- a/R/bound_prediction.R +++ b/R/bound_prediction.R @@ -1,11 +1,11 @@ #' Truncate a numeric prediction column #' -#' For user-defined lower_limit and/or upper_limit bound, ensure that the values in the +#' For user-defined `lower_limit` and/or `upper_limit` bound, ensure that the values in the #' `.pred` column are coerced to these bounds. #' #' @param x A data frame that contains a numeric column named `.pred`. #' @param lower_limit,upper_limit Single numerics (or `NA`) that define -#' constrains on `.pred`. +#' constraints on `.pred`. #' @param call The call to be displayed in warnings or errors. #' @return `x` with potentially adjusted values. #' @examples @@ -16,9 +16,9 @@ #' bound_prediction(solubility_test, lower_limit = -1) #' @export bound_prediction <- function(x, lower_limit = -Inf, upper_limit = Inf, - call = rlang::caller_env()) { + call = rlang::current_env()) { if (!any(names(x) == ".pred")) { - cli::cli_abort("The argument {.arg x} should have a column named {.code .pred}", + cli::cli_abort("The argument {.arg x} should have a column named {.code .pred}.", call = call) } if (!is.numeric(x$.pred)) { From 82d2399fc73a7f4c56ee38f771c53a556c84ffee Mon Sep 17 00:00:00 2001 From: topepo Date: Thu, 4 Apr 2024 14:23:42 -0400 Subject: [PATCH 06/10] cleanup --- tests/testthat/Rplots.pdf | Bin 54039 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tests/testthat/Rplots.pdf diff --git a/tests/testthat/Rplots.pdf b/tests/testthat/Rplots.pdf deleted file mode 100644 index 14a622dba84b05767be9fe5d78d7cd237622c6ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54039 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

>kqeB#PwyQEs&~hj`Wf^d0s6 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 Date: Thu, 4 Apr 2024 14:35:33 -0400 Subject: [PATCH 07/10] rlang type checkers --- DESCRIPTION | 2 +- R/bound_prediction.R | 12 +- R/import-standalone-obj-type.R | 360 +++++++++++++++ R/import-standalone-types-check.R | 538 ++++++++++++++++++++++ tests/testthat/_snaps/bound-prediction.md | 36 +- tests/testthat/test-bound-prediction.R | 5 +- 6 files changed, 940 insertions(+), 13 deletions(-) create mode 100644 R/import-standalone-obj-type.R create mode 100644 R/import-standalone-types-check.R diff --git a/DESCRIPTION b/DESCRIPTION index 82b2437a..1bc1a227 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -28,7 +28,7 @@ Imports: hardhat, pillar, purrr, - rlang (>= 1.0.4), + rlang (>= 1.1.0), tidyr (>= 1.3.0), tidyselect (>= 1.1.2), tune (>= 1.1.2), diff --git a/R/bound_prediction.R b/R/bound_prediction.R index e1ccfec1..7513ac51 100644 --- a/R/bound_prediction.R +++ b/R/bound_prediction.R @@ -17,20 +17,24 @@ #' @export bound_prediction <- function(x, lower_limit = -Inf, upper_limit = Inf, call = rlang::current_env()) { + check_data_frame(x, call = call) + if (!any(names(x) == ".pred")) { cli::cli_abort("The argument {.arg x} should have a column named {.code .pred}.", call = call) } if (!is.numeric(x$.pred)) { - cli::cli_abort("Column {.code .pred} should be numeric.", - call = call) + cli::cli_abort("Column {.code .pred} should be numeric.", call = call) } - if (is.numeric(lower_limit) && !is.na(lower_limit)) { + check_number_decimal(lower_limit, allow_na = TRUE, call = call) + check_number_decimal(upper_limit, allow_na = TRUE, call = call) + + if (!is.na(lower_limit)) { x$.pred <- ifelse(x$.pred < lower_limit, lower_limit, x$.pred) } - if (is.numeric(upper_limit) && !is.na(upper_limit)) { + if (!is.na(upper_limit)) { x$.pred <- ifelse(x$.pred > upper_limit, upper_limit, x$.pred) } x diff --git a/R/import-standalone-obj-type.R b/R/import-standalone-obj-type.R new file mode 100644 index 00000000..8e3c07df --- /dev/null +++ b/R/import-standalone-obj-type.R @@ -0,0 +1,360 @@ +# Standalone file: do not edit by hand +# Source: +# ---------------------------------------------------------------------- +# +# --- +# repo: r-lib/rlang +# file: standalone-obj-type.R +# last-updated: 2023-05-01 +# license: https://unlicense.org +# imports: rlang (>= 1.1.0) +# --- +# +# ## Changelog +# +# 2023-05-01: +# - `obj_type_friendly()` now only displays the first class of S3 objects. +# +# 2023-03-30: +# - `stop_input_type()` now handles `I()` input literally in `arg`. +# +# 2022-10-04: +# - `obj_type_friendly(value = TRUE)` now shows numeric scalars +# literally. +# - `stop_friendly_type()` now takes `show_value`, passed to +# `obj_type_friendly()` as the `value` argument. +# +# 2022-10-03: +# - Added `allow_na` and `allow_null` arguments. +# - `NULL` is now backticked. +# - Better friendly type for infinities and `NaN`. +# +# 2022-09-16: +# - Unprefixed usage of rlang functions with `rlang::` to +# avoid onLoad issues when called from rlang (#1482). +# +# 2022-08-11: +# - Prefixed usage of rlang functions with `rlang::`. +# +# 2022-06-22: +# - `friendly_type_of()` is now `obj_type_friendly()`. +# - Added `obj_type_oo()`. +# +# 2021-12-20: +# - Added support for scalar values and empty vectors. +# - Added `stop_input_type()` +# +# 2021-06-30: +# - Added support for missing arguments. +# +# 2021-04-19: +# - Added support for matrices and arrays (#141). +# - Added documentation. +# - Added changelog. +# +# nocov start + +#' Return English-friendly type +#' @param x Any R object. +#' @param value Whether to describe the value of `x`. Special values +#' like `NA` or `""` are always described. +#' @param length Whether to mention the length of vectors and lists. +#' @return A string describing the type. Starts with an indefinite +#' article, e.g. "an integer vector". +#' @noRd +obj_type_friendly <- function(x, value = TRUE) { + if (is_missing(x)) { + return("absent") + } + + if (is.object(x)) { + if (inherits(x, "quosure")) { + type <- "quosure" + } else { + type <- class(x)[[1L]] + } + return(sprintf("a <%s> object", type)) + } + + if (!is_vector(x)) { + return(.rlang_as_friendly_type(typeof(x))) + } + + n_dim <- length(dim(x)) + + if (!n_dim) { + if (!is_list(x) && length(x) == 1) { + if (is_na(x)) { + return(switch( + typeof(x), + logical = "`NA`", + integer = "an integer `NA`", + double = + if (is.nan(x)) { + "`NaN`" + } else { + "a numeric `NA`" + }, + complex = "a complex `NA`", + character = "a character `NA`", + .rlang_stop_unexpected_typeof(x) + )) + } + + show_infinites <- function(x) { + if (x > 0) { + "`Inf`" + } else { + "`-Inf`" + } + } + str_encode <- function(x, width = 30, ...) { + if (nchar(x) > width) { + x <- substr(x, 1, width - 3) + x <- paste0(x, "...") + } + encodeString(x, ...) + } + + if (value) { + if (is.numeric(x) && is.infinite(x)) { + return(show_infinites(x)) + } + + if (is.numeric(x) || is.complex(x)) { + number <- as.character(round(x, 2)) + what <- if (is.complex(x)) "the complex number" else "the number" + return(paste(what, number)) + } + + return(switch( + typeof(x), + logical = if (x) "`TRUE`" else "`FALSE`", + character = { + what <- if (nzchar(x)) "the string" else "the empty string" + paste(what, str_encode(x, quote = "\"")) + }, + raw = paste("the raw value", as.character(x)), + .rlang_stop_unexpected_typeof(x) + )) + } + + return(switch( + typeof(x), + logical = "a logical value", + integer = "an integer", + double = if (is.infinite(x)) show_infinites(x) else "a number", + complex = "a complex number", + character = if (nzchar(x)) "a string" else "\"\"", + raw = "a raw value", + .rlang_stop_unexpected_typeof(x) + )) + } + + if (length(x) == 0) { + return(switch( + typeof(x), + logical = "an empty logical vector", + integer = "an empty integer vector", + double = "an empty numeric vector", + complex = "an empty complex vector", + character = "an empty character vector", + raw = "an empty raw vector", + list = "an empty list", + .rlang_stop_unexpected_typeof(x) + )) + } + } + + vec_type_friendly(x) +} + +vec_type_friendly <- function(x, length = FALSE) { + if (!is_vector(x)) { + abort("`x` must be a vector.") + } + type <- typeof(x) + n_dim <- length(dim(x)) + + add_length <- function(type) { + if (length && !n_dim) { + paste0(type, sprintf(" of length %s", length(x))) + } else { + type + } + } + + if (type == "list") { + if (n_dim < 2) { + return(add_length("a list")) + } else if (is.data.frame(x)) { + return("a data frame") + } else if (n_dim == 2) { + return("a list matrix") + } else { + return("a list array") + } + } + + type <- switch( + type, + logical = "a logical %s", + integer = "an integer %s", + numeric = , + double = "a double %s", + complex = "a complex %s", + character = "a character %s", + raw = "a raw %s", + type = paste0("a ", type, " %s") + ) + + if (n_dim < 2) { + kind <- "vector" + } else if (n_dim == 2) { + kind <- "matrix" + } else { + kind <- "array" + } + out <- sprintf(type, kind) + + if (n_dim >= 2) { + out + } else { + add_length(out) + } +} + +.rlang_as_friendly_type <- function(type) { + switch( + type, + + list = "a list", + + NULL = "`NULL`", + environment = "an environment", + externalptr = "a pointer", + weakref = "a weak reference", + S4 = "an S4 object", + + name = , + symbol = "a symbol", + language = "a call", + pairlist = "a pairlist node", + expression = "an expression vector", + + char = "an internal string", + promise = "an internal promise", + ... = "an internal dots object", + any = "an internal `any` object", + bytecode = "an internal bytecode object", + + primitive = , + builtin = , + special = "a primitive function", + closure = "a function", + + type + ) +} + +.rlang_stop_unexpected_typeof <- function(x, call = caller_env()) { + abort( + sprintf("Unexpected type <%s>.", typeof(x)), + call = call + ) +} + +#' Return OO type +#' @param x Any R object. +#' @return One of `"bare"` (for non-OO objects), `"S3"`, `"S4"`, +#' `"R6"`, or `"R7"`. +#' @noRd +obj_type_oo <- function(x) { + if (!is.object(x)) { + return("bare") + } + + class <- inherits(x, c("R6", "R7_object"), which = TRUE) + + if (class[[1]]) { + "R6" + } else if (class[[2]]) { + "R7" + } else if (isS4(x)) { + "S4" + } else { + "S3" + } +} + +#' @param x The object type which does not conform to `what`. Its +#' `obj_type_friendly()` is taken and mentioned in the error message. +#' @param what The friendly expected type as a string. Can be a +#' character vector of expected types, in which case the error +#' message mentions all of them in an "or" enumeration. +#' @param show_value Passed to `value` argument of `obj_type_friendly()`. +#' @param ... Arguments passed to [abort()]. +#' @inheritParams args_error_context +#' @noRd +stop_input_type <- function(x, + what, + ..., + allow_na = FALSE, + allow_null = FALSE, + show_value = TRUE, + arg = caller_arg(x), + call = caller_env()) { + # From standalone-cli.R + cli <- env_get_list( + nms = c("format_arg", "format_code"), + last = topenv(), + default = function(x) sprintf("`%s`", x), + inherit = TRUE + ) + + if (allow_na) { + what <- c(what, cli$format_code("NA")) + } + if (allow_null) { + what <- c(what, cli$format_code("NULL")) + } + if (length(what)) { + what <- oxford_comma(what) + } + if (inherits(arg, "AsIs")) { + format_arg <- identity + } else { + format_arg <- cli$format_arg + } + + message <- sprintf( + "%s must be %s, not %s.", + format_arg(arg), + what, + obj_type_friendly(x, value = show_value) + ) + + abort(message, ..., call = call, arg = arg) +} + +oxford_comma <- function(chr, sep = ", ", final = "or") { + n <- length(chr) + + if (n < 2) { + return(chr) + } + + head <- chr[seq_len(n - 1)] + last <- chr[n] + + head <- paste(head, collapse = sep) + + # Write a or b. But a, b, or c. + if (n > 2) { + paste0(head, sep, final, " ", last) + } else { + paste0(head, " ", final, " ", last) + } +} + +# nocov end diff --git a/R/import-standalone-types-check.R b/R/import-standalone-types-check.R new file mode 100644 index 00000000..6782d69b --- /dev/null +++ b/R/import-standalone-types-check.R @@ -0,0 +1,538 @@ +# Standalone file: do not edit by hand +# Source: +# ---------------------------------------------------------------------- +# +# --- +# repo: r-lib/rlang +# file: standalone-types-check.R +# last-updated: 2023-03-13 +# license: https://unlicense.org +# dependencies: standalone-obj-type.R +# imports: rlang (>= 1.1.0) +# --- +# +# ## Changelog +# +# 2023-03-13: +# - Improved error messages of number checkers (@teunbrand) +# - Added `allow_infinite` argument to `check_number_whole()` (@mgirlich). +# - Added `check_data_frame()` (@mgirlich). +# +# 2023-03-07: +# - Added dependency on rlang (>= 1.1.0). +# +# 2023-02-15: +# - Added `check_logical()`. +# +# - `check_bool()`, `check_number_whole()`, and +# `check_number_decimal()` are now implemented in C. +# +# - For efficiency, `check_number_whole()` and +# `check_number_decimal()` now take a `NULL` default for `min` and +# `max`. This makes it possible to bypass unnecessary type-checking +# and comparisons in the default case of no bounds checks. +# +# 2022-10-07: +# - `check_number_whole()` and `_decimal()` no longer treat +# non-numeric types such as factors or dates as numbers. Numeric +# types are detected with `is.numeric()`. +# +# 2022-10-04: +# - Added `check_name()` that forbids the empty string. +# `check_string()` allows the empty string by default. +# +# 2022-09-28: +# - Removed `what` arguments. +# - Added `allow_na` and `allow_null` arguments. +# - Added `allow_decimal` and `allow_infinite` arguments. +# - Improved errors with absent arguments. +# +# +# 2022-09-16: +# - Unprefixed usage of rlang functions with `rlang::` to +# avoid onLoad issues when called from rlang (#1482). +# +# 2022-08-11: +# - Added changelog. +# +# nocov start + +# Scalars ----------------------------------------------------------------- + +.standalone_types_check_dot_call <- .Call + +check_bool <- function(x, + ..., + allow_na = FALSE, + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env()) { + if (!missing(x) && .standalone_types_check_dot_call(ffi_standalone_is_bool_1.0.7, x, allow_na, allow_null)) { + return(invisible(NULL)) + } + + stop_input_type( + x, + c("`TRUE`", "`FALSE`"), + ..., + allow_na = allow_na, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_string <- function(x, + ..., + allow_empty = TRUE, + allow_na = FALSE, + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env()) { + if (!missing(x)) { + is_string <- .rlang_check_is_string( + x, + allow_empty = allow_empty, + allow_na = allow_na, + allow_null = allow_null + ) + if (is_string) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a single string", + ..., + allow_na = allow_na, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +.rlang_check_is_string <- function(x, + allow_empty, + allow_na, + allow_null) { + if (is_string(x)) { + if (allow_empty || !is_string(x, "")) { + return(TRUE) + } + } + + if (allow_null && is_null(x)) { + return(TRUE) + } + + if (allow_na && (identical(x, NA) || identical(x, na_chr))) { + return(TRUE) + } + + FALSE +} + +check_name <- function(x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env()) { + if (!missing(x)) { + is_string <- .rlang_check_is_string( + x, + allow_empty = FALSE, + allow_na = FALSE, + allow_null = allow_null + ) + if (is_string) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a valid name", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +IS_NUMBER_true <- 0 +IS_NUMBER_false <- 1 +IS_NUMBER_oob <- 2 + +check_number_decimal <- function(x, + ..., + min = NULL, + max = NULL, + allow_infinite = TRUE, + allow_na = FALSE, + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env()) { + if (missing(x)) { + exit_code <- IS_NUMBER_false + } else if (0 == (exit_code <- .standalone_types_check_dot_call( + ffi_standalone_check_number_1.0.7, + x, + allow_decimal = TRUE, + min, + max, + allow_infinite, + allow_na, + allow_null + ))) { + return(invisible(NULL)) + } + + .stop_not_number( + x, + ..., + exit_code = exit_code, + allow_decimal = TRUE, + min = min, + max = max, + allow_na = allow_na, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_number_whole <- function(x, + ..., + min = NULL, + max = NULL, + allow_infinite = FALSE, + allow_na = FALSE, + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env()) { + if (missing(x)) { + exit_code <- IS_NUMBER_false + } else if (0 == (exit_code <- .standalone_types_check_dot_call( + ffi_standalone_check_number_1.0.7, + x, + allow_decimal = FALSE, + min, + max, + allow_infinite, + allow_na, + allow_null + ))) { + return(invisible(NULL)) + } + + .stop_not_number( + x, + ..., + exit_code = exit_code, + allow_decimal = FALSE, + min = min, + max = max, + allow_na = allow_na, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +.stop_not_number <- function(x, + ..., + exit_code, + allow_decimal, + min, + max, + allow_na, + allow_null, + arg, + call) { + if (allow_decimal) { + what <- "a number" + } else { + what <- "a whole number" + } + + if (exit_code == IS_NUMBER_oob) { + min <- min %||% -Inf + max <- max %||% Inf + + if (min > -Inf && max < Inf) { + what <- sprintf("%s between %s and %s", what, min, max) + } else if (x < min) { + what <- sprintf("%s larger than or equal to %s", what, min) + } else if (x > max) { + what <- sprintf("%s smaller than or equal to %s", what, max) + } else { + abort("Unexpected state in OOB check", .internal = TRUE) + } + } + + stop_input_type( + x, + what, + ..., + allow_na = allow_na, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_symbol <- function(x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env()) { + if (!missing(x)) { + if (is_symbol(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a symbol", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_arg <- function(x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env()) { + if (!missing(x)) { + if (is_symbol(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "an argument name", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_call <- function(x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env()) { + if (!missing(x)) { + if (is_call(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a defused call", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_environment <- function(x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env()) { + if (!missing(x)) { + if (is_environment(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "an environment", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_function <- function(x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env()) { + if (!missing(x)) { + if (is_function(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a function", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_closure <- function(x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env()) { + if (!missing(x)) { + if (is_closure(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "an R function", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_formula <- function(x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env()) { + if (!missing(x)) { + if (is_formula(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a formula", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + + +# Vectors ----------------------------------------------------------------- + +check_character <- function(x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env()) { + if (!missing(x)) { + if (is_character(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a character vector", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_logical <- function(x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env()) { + if (!missing(x)) { + if (is_logical(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a logical vector", + ..., + allow_na = FALSE, + allow_null = allow_null, + arg = arg, + call = call + ) +} + +check_data_frame <- function(x, + ..., + allow_null = FALSE, + arg = caller_arg(x), + call = caller_env()) { + if (!missing(x)) { + if (is.data.frame(x)) { + return(invisible(NULL)) + } + if (allow_null && is_null(x)) { + return(invisible(NULL)) + } + } + + stop_input_type( + x, + "a data frame", + ..., + allow_null = allow_null, + arg = arg, + call = call + ) +} + +# nocov end diff --git a/tests/testthat/_snaps/bound-prediction.md b/tests/testthat/_snaps/bound-prediction.md index 37de55c1..060673ca 100644 --- a/tests/testthat/_snaps/bound-prediction.md +++ b/tests/testthat/_snaps/bound-prediction.md @@ -3,8 +3,8 @@ Code bound_prediction(solubility_test, lower_limit = 2) Condition - Error: - ! The argument `x` should have a column named `.pred` + Error in `bound_prediction()`: + ! The argument `x` should have a column named `.pred`. --- @@ -12,16 +12,32 @@ solubility_test %>% mutate(.pred = format(prediction)) %>% bound_prediction( lower_limit = 2) Condition - Error: + Error in `bound_prediction()`: ! Column `.pred` should be numeric. +--- + + Code + bound_prediction(sol, lower_limit = tune2()) + Condition + Error in `bound_prediction()`: + ! `lower_limit` must be a number or `NA`, not a call. + +--- + + Code + bound_prediction(as.matrix(sol), lower_limit = 1) + Condition + Error in `bound_prediction()`: + ! `x` must be a data frame, not a double matrix. + # upper_limit bounds for numeric predictions Code bound_prediction(solubility_test, lower_limit = 2) Condition - Error: - ! The argument `x` should have a column named `.pred` + Error in `bound_prediction()`: + ! The argument `x` should have a column named `.pred`. --- @@ -29,6 +45,14 @@ solubility_test %>% mutate(.pred = format(prediction)) %>% bound_prediction( lower_limit = 2) Condition - Error: + Error in `bound_prediction()`: ! Column `.pred` should be numeric. +--- + + Code + bound_prediction(sol, upper_limit = tune2()) + Condition + Error in `bound_prediction()`: + ! `upper_limit` must be a number or `NA`, not a call. + diff --git a/tests/testthat/test-bound-prediction.R b/tests/testthat/test-bound-prediction.R index a9fc51c7..3118691a 100644 --- a/tests/testthat/test-bound-prediction.R +++ b/tests/testthat/test-bound-prediction.R @@ -23,7 +23,8 @@ test_that("lower_limit bounds for numeric predictions", { expect_true(all(res_1$.pred[res_1$.pred < -1] == -1)) expect_true(all(res_1$.pred[res_1$.pred >= -1] == res_1$.pred[res_1$.pred >= -1])) - expect_equal(bound_prediction(sol, lower_limit = tune2()), sol) + expect_snapshot(bound_prediction(sol, lower_limit = tune2()), error = TRUE) + expect_snapshot(bound_prediction(as.matrix(sol), lower_limit = 1), error = TRUE) }) test_that("upper_limit bounds for numeric predictions", { @@ -51,5 +52,5 @@ test_that("upper_limit bounds for numeric predictions", { expect_true(all(res_1$.pred[res_1$.pred > -1] == -1)) expect_true(all(res_1$.pred[res_1$.pred <= -1] == res_1$.pred[res_1$.pred <= -1])) - expect_equal(bound_prediction(sol, upper_limit = tune2()), sol) + expect_snapshot(bound_prediction(sol, upper_limit = tune2()), error = TRUE) }) From da49979818aadfebad078367c052938fe6ccd69a Mon Sep 17 00:00:00 2001 From: topepo Date: Thu, 4 Apr 2024 14:48:58 -0400 Subject: [PATCH 08/10] re-doc --- man/bound_prediction.Rd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/man/bound_prediction.Rd b/man/bound_prediction.Rd index f24b4819..84de0ef0 100644 --- a/man/bound_prediction.Rd +++ b/man/bound_prediction.Rd @@ -8,14 +8,14 @@ bound_prediction( x, lower_limit = -Inf, upper_limit = Inf, - call = rlang::caller_env() + call = rlang::current_env() ) } \arguments{ \item{x}{A data frame that contains a numeric column named \code{.pred}.} \item{lower_limit, upper_limit}{Single numerics (or \code{NA}) that define -constrains on \code{.pred}.} +constraints on \code{.pred}.} \item{call}{The call to be displayed in warnings or errors.} } @@ -23,7 +23,7 @@ constrains on \code{.pred}.} \code{x} with potentially adjusted values. } \description{ -For user-defined lower_limit and/or upper_limit bound, ensure that the values in the +For user-defined \code{lower_limit} and/or \code{upper_limit} bound, ensure that the values in the \code{.pred} column are coerced to these bounds. } \examples{ From 2aa64fa8e59acceffdf7d0fb6db082633e16f140 Mon Sep 17 00:00:00 2001 From: Max Kuhn Date: Thu, 4 Apr 2024 15:00:31 -0400 Subject: [PATCH 09/10] Apply suggestions from code review Co-authored-by: Simon P. Couch --- tests/testthat/test-bound-prediction.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test-bound-prediction.R b/tests/testthat/test-bound-prediction.R index 3118691a..17834a54 100644 --- a/tests/testthat/test-bound-prediction.R +++ b/tests/testthat/test-bound-prediction.R @@ -21,7 +21,7 @@ test_that("lower_limit bounds for numeric predictions", { res_1 <- bound_prediction(sol, lower_limit = -1) expect_true(all(res_1$.pred[res_1$.pred < -1] == -1)) - expect_true(all(res_1$.pred[res_1$.pred >= -1] == res_1$.pred[res_1$.pred >= -1])) + expect_true(all(res_1$.pred[res_1$.pred >= -1] == sol$.pred[sol$.pred >= -1])) expect_snapshot(bound_prediction(sol, lower_limit = tune2()), error = TRUE) expect_snapshot(bound_prediction(as.matrix(sol), lower_limit = 1), error = TRUE) @@ -50,7 +50,7 @@ test_that("upper_limit bounds for numeric predictions", { res_1 <- bound_prediction(sol, upper_limit = -1) expect_true(all(res_1$.pred[res_1$.pred > -1] == -1)) - expect_true(all(res_1$.pred[res_1$.pred <= -1] == res_1$.pred[res_1$.pred <= -1])) + expect_true(all(res_1$.pred[res_1$.pred <= -1] == sol$.pred[sol$.pred <= -1])) expect_snapshot(bound_prediction(sol, upper_limit = tune2()), error = TRUE) }) From fc1bea05885055a1b21872182144a6742cb6cc27 Mon Sep 17 00:00:00 2001 From: topepo Date: Thu, 4 Apr 2024 15:02:10 -0400 Subject: [PATCH 10/10] update tests --- tests/testthat/test-bound-prediction.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test-bound-prediction.R b/tests/testthat/test-bound-prediction.R index 17834a54..c274dcfa 100644 --- a/tests/testthat/test-bound-prediction.R +++ b/tests/testthat/test-bound-prediction.R @@ -21,7 +21,7 @@ test_that("lower_limit bounds for numeric predictions", { res_1 <- bound_prediction(sol, lower_limit = -1) expect_true(all(res_1$.pred[res_1$.pred < -1] == -1)) - expect_true(all(res_1$.pred[res_1$.pred >= -1] == sol$.pred[sol$.pred >= -1])) + expect_equal(res_1$.pred[sol$.pred >= -1], sol$.pred[sol$.pred >= -1]) expect_snapshot(bound_prediction(sol, lower_limit = tune2()), error = TRUE) expect_snapshot(bound_prediction(as.matrix(sol), lower_limit = 1), error = TRUE) @@ -50,7 +50,7 @@ test_that("upper_limit bounds for numeric predictions", { res_1 <- bound_prediction(sol, upper_limit = -1) expect_true(all(res_1$.pred[res_1$.pred > -1] == -1)) - expect_true(all(res_1$.pred[res_1$.pred <= -1] == sol$.pred[sol$.pred <= -1])) + expect_equal(res_1$.pred[sol$.pred <= -1], sol$.pred[sol$.pred <= -1]) expect_snapshot(bound_prediction(sol, upper_limit = tune2()), error = TRUE) })