From 69f1351dbb9a4bdef21a84f5bc48193959125689 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=98topepo=E2=80=99?= <‘mxkuhn@gmail.com’>
Date: Mon, 15 Apr 2024 13:34:42 -0400
Subject: [PATCH 01/39] initial versions
---
DESCRIPTION | 9 +-
NAMESPACE | 27 ++++++
R/container-package.R | 4 +
R/container.R | 157 +++++++++++++++++++++++++++++++++++
R/operation.R | 17 ++++
R/prob_threshold.R | 90 ++++++++++++++++++++
R/reexport.R | 32 +++++++
man/adjust_prob_threshold.Rd | 36 ++++++++
man/container-package.Rd | 1 -
man/container.Rd | 39 +++++++++
man/reexports.Rd | 27 ++++++
tests/testthat.R | 2 +-
12 files changed, 438 insertions(+), 3 deletions(-)
create mode 100644 R/container.R
create mode 100644 R/operation.R
create mode 100644 R/prob_threshold.R
create mode 100644 R/reexport.R
create mode 100644 man/adjust_prob_threshold.Rd
create mode 100644 man/container.Rd
create mode 100644 man/reexports.Rd
diff --git a/DESCRIPTION b/DESCRIPTION
index bbc4856..b04fd3d 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -11,6 +11,7 @@ Authors@R: c(
Description: Sandbox for a postprocessor object.
License: MIT + file LICENSE
Suggests:
+ modeldata,
testthat (>= 3.0.0)
Config/testthat/edition: 3
Encoding: UTF-8
@@ -20,4 +21,10 @@ URL: https://github.com/tidymodels/container
BugReports: https://github.com/tidymodels/container/issues
Imports:
cli,
- rlang (>= 1.1.0)
+ dplyr,
+ generics,
+ hardhat,
+ purrr,
+ rlang (>= 1.1.0),
+ tibble,
+ tidyselect
diff --git a/NAMESPACE b/NAMESPACE
index 20159f1..8c38bfd 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -1,6 +1,33 @@
# Generated by roxygen2: do not edit by hand
+S3method(fit,container)
+S3method(fit,prob_threshold)
+S3method(predict,container)
+S3method(predict,prob_threshold)
+S3method(print,container)
+S3method(print,prob_threshold)
+S3method(required_pkgs,prob_threshold)
+S3method(tunable,prob_threshold)
+export("%>%")
+export(adjust_prob_threshold)
+export(container)
+export(extract_parameter_dials)
+export(extract_parameter_set_dials)
+export(fit)
+export(required_pkgs)
+export(tidy)
+export(tunable)
+export(tune_args)
import(rlang)
importFrom(cli,cli_abort)
importFrom(cli,cli_inform)
importFrom(cli,cli_warn)
+importFrom(dplyr,"%>%")
+importFrom(generics,fit)
+importFrom(generics,required_pkgs)
+importFrom(generics,tidy)
+importFrom(generics,tunable)
+importFrom(generics,tune_args)
+importFrom(hardhat,extract_parameter_dials)
+importFrom(hardhat,extract_parameter_set_dials)
+importFrom(stats,predict)
diff --git a/R/container-package.R b/R/container-package.R
index 9e15a93..ca10c39 100644
--- a/R/container-package.R
+++ b/R/container-package.R
@@ -1,8 +1,12 @@
#' @import rlang
#' @importFrom cli cli_abort cli_warn cli_inform
+#' @importFrom stats predict
#' @keywords internal
"_PACKAGE"
## usethis namespace: start
+utils::globalVariables("data")
## usethis namespace: end
NULL
+
+
diff --git a/R/container.R b/R/container.R
new file mode 100644
index 0000000..cbec12f
--- /dev/null
+++ b/R/container.R
@@ -0,0 +1,157 @@
+#' Declare post-processing for model predictions
+#'
+#' @param mode The model's mode, one of `"unknown"`, `"classification"`, or
+#' `"regression"`. Modes of `"censored regression"` are not currently supported.
+#' @param type The model sub-type. Possible values are `"unknown"`, `"regression"`,
+#' `"binary"`, or `"multiclass"`.
+#' @param outcome The name of the outcome variable.
+#' @param estimate The name of the point estimate (e.g. predicted class)
+#' @param probabilities The names of class probability estimates (if any). For
+#' classification, these should be given in the order of the factor levels of
+#' the `estimate`.
+#' @param time The name of the predicted event time.
+#' @examples
+#'
+#' container()
+#' @export
+container <- function(mode = "unknown", type = "unknown", outcome = character(0),
+ estimate = character(0), probabilities = character(0),
+ time = character(0)) {
+ dat <-
+ list(
+ outcome = outcome,
+ type = type,
+ estimate = estimate,
+ probabilities = probabilities,
+ time = time
+ )
+ new_container(mode, type, operations = list(), columns = dat, ptype = tibble::tibble())
+}
+
+new_container <- function(mode, type, operations, columns, ptype) {
+ mode <- rlang::arg_match0(mode, c("unknown", "regression", "classification", "censored regression"))
+ type <- rlang::arg_match0(type, c("unknown", "regression", "binary", "multiclass"))
+
+ if ( !is.list(operations) ) {
+
+ }
+
+ is_oper <- purrr::map_lgl(operations, ~ inherits(.x, "operation"))
+ if ( !any(is_oper) ) {
+
+ }
+
+ # check columns
+
+ # validate operation order and check duplicates
+
+ res <- list(mode = mode, type = type, operations = operations,
+ columns = columns, ptype = ptype)
+ class(res) <- "container"
+ res
+}
+
+#' @export
+print.container <- function(x, ...) {
+ # todo emulate Emil's recipe printing
+
+ num_op <- length(x$operations)
+ cli::cli_inform("{x$type} post-processing object with {num_op} operation{?s}")
+
+ if (num_op > 0) {
+ cat("\n")
+ res <- purrr::map(x$operations, ~ print(.x))
+ }
+
+ invisible(x)
+}
+
+
+# ------------------------------------------------------------------------------
+
+#' @export
+fit.container <- function(object, .data, outcome, estimate, probabilities = c(),
+ time = c(), ...) {
+
+ # ------------------------------------------------------------------------------
+ # set columns via tidyselect
+
+ dat <- list()
+ dat$outcome <- names(tidyselect::eval_select(rlang::enquo(outcome), .data))
+ dat$estimate <- names(tidyselect::eval_select(rlang::enquo(estimate), .data))
+
+ probabilities <- tidyselect::eval_select(rlang::enquo(probabilities), .data)
+ if (length(probabilities) > 0) {
+ dat$probabilities <- names(probabilities)
+ } else {
+ dat$probabilities <- character(0)
+ }
+
+ time <- tidyselect::eval_select(rlang::enquo(time), .data)
+ if (length(time) > 0) {
+ dat$time <- names(time)
+ } else {
+ dat$time <- character(0)
+ }
+
+ .data <- .data[, names(.data) %in% unlist(dat)]
+ .data <- tibble::as_tibble(.data)
+ ptype <- .data[0,]
+
+
+
+ object <- set_container_type(object, .data[[ dat$outcome ]])
+
+ object <- new_container(object$mode, object$type,
+ operations = object$operations,
+ columns = dat, ptype = ptype)
+
+ # ------------------------------------------------------------------------------
+
+ num_oper <- length(object$operations)
+ for (op in 1:num_oper) {
+ object$operations[[op]] <- fit(object$operations[[op]], data, object)
+ .data <- predict(object$operations[[op]], .data, object)
+ }
+
+ # todo Add a fitted container class?
+ object
+}
+
+#' @export
+predict.container <- function(object, new_data, ...) {
+
+ # validate levels/classes
+ num_oper <- length(object$operations)
+ for (op in 1:num_oper) {
+ new_data <- predict(object$operations[[op]], new_data, object)
+ }
+ tibble::as_tibble(new_data)
+}
+
+set_container_type <- function(object, y) {
+ if (object$type != "unknown") {
+ return(object)
+ }
+ if (is.factor(y)) {
+ lvls <- levels(y)
+ if (length(lvls) == 2) {
+ object$type <- "binary"
+ } else {
+ object$type <- "multiclass"
+ }
+ } else if (is.numeric(y)) {
+ object$type <- "regression"
+ } else {
+ cli::cli_abort("Only factor and numeric outcomes are currently supported.")
+ }
+ object
+}
+
+# todo: where to validate #levels?
+# todo setup eval_time
+# todo missing methods:
+# todo tune_args
+# todo tidy
+# todo extract_parameter_set_dials
+
diff --git a/R/operation.R b/R/operation.R
new file mode 100644
index 0000000..e9bbdc8
--- /dev/null
+++ b/R/operation.R
@@ -0,0 +1,17 @@
+
+input_vals <- c("numeric", "probability", "class")
+output_vals <- c("numeric", "probability", "class")
+
+new_operation <- function(cls, inputs, outputs, arguments, results = list(trained = FALSE), ...) {
+ # check outputs too: multi choice
+ inputs <- rlang::arg_match0(inputs, input_vals)
+ res <-
+ list(
+ inputs = inputs,
+ outputs = sort(outputs),
+ arguments = arguments,
+ results = results
+ )
+ class(res) <- cls
+ res
+}
diff --git a/R/prob_threshold.R b/R/prob_threshold.R
new file mode 100644
index 0000000..e448afe
--- /dev/null
+++ b/R/prob_threshold.R
@@ -0,0 +1,90 @@
+#' Change the event threshold
+#'
+#' @param x A [container()].
+#' @param threshold A numeric value (between zero and one) or [hardhat::tune()].
+#' @examples
+#' library(dplyr)
+#' library(modeldata)
+#'
+#' post_obj <-
+#' container(mode = "classification") %>%
+#' adjust_prob_threshold(threshold = .1)
+#'
+#' two_class_example %>% count(predicted)
+#'
+#' post_res <- fit(
+#' post_obj,
+#' two_class_example,
+#' outcome = c(truth),
+#' estimate = c(predicted),
+#' probabilities = c(Class1, Class2)
+#' )
+#'
+#' predict(post_res, two_class_example) %>% count(predicted)
+#' @export
+adjust_prob_threshold <- function(x, threshold = 0.5) {
+ op <-
+ new_operation(
+ "prob_threshold",
+ inputs = "probability",
+ outputs = c("probability", "class"),
+ arguments = list(threshold = threshold),
+ results = list()
+ )
+ x$operations <- c(x$operations, list(op))
+ x
+}
+
+#' @export
+print.prob_threshold <- function(x, ...) {
+ # check for tune() first
+
+ trn <- ifelse(x$results$trained, " [trained]", "")
+
+ cli::cli_inform(c("Adjust probability threshold to \\
+ {signif(x$arguments$threshold, digits = 3)}{trn}"))
+ invisible(x)
+}
+
+#' @export
+fit.prob_threshold <- function(object, data, parent = NULL, ...) {
+ new_operation(
+ class(object),
+ inputs = object$inputs,
+ outputs = object$outputs,
+ arguments = object$arguments,
+ results = list(trained = TRUE)
+ )
+}
+
+#' @export
+predict.prob_threshold <- function(object, new_data, parent, ...) {
+ est_nm <- parent$columns$estimate
+ prob_nm <- parent$columns$probabilities[1]
+ lvls <- levels(new_data[[ est_nm ]])
+
+ new_data[[ est_nm ]] <-
+ ifelse(new_data[[ prob_nm ]] >= object$arguments$threshold, lvls[1], lvls[2])
+ new_data[[ est_nm ]] <- factor(new_data[[ est_nm ]], levels = lvls)
+ new_data
+}
+
+#' @export
+required_pkgs.prob_threshold <- function(x, ...) {
+ c("container")
+}
+
+#' @export
+tunable.prob_threshold <- function(x, ...) {
+ tibble::tibble(
+ name = "threshold",
+ call_info = list(list(pkg = "dials", fun = "threshold")),
+ source = "container",
+ component = "prob_threshold",
+ component_id = "prob_threshold")
+}
+
+# todo missing methods:
+# todo tune_args
+# todo tidy
+# todo extract_parameter_set_dials
diff --git a/R/reexport.R b/R/reexport.R
new file mode 100644
index 0000000..8f3dff8
--- /dev/null
+++ b/R/reexport.R
@@ -0,0 +1,32 @@
+
+#' @importFrom generics fit
+#' @export
+generics::fit
+
+#' @importFrom generics tidy
+#' @export
+generics::tidy
+
+#' @importFrom generics required_pkgs
+#' @export
+generics::required_pkgs
+
+#' @importFrom generics tunable
+#' @export
+generics::tunable
+
+#' @importFrom generics tune_args
+#' @export
+generics::tune_args
+
+#' @importFrom hardhat extract_parameter_set_dials
+#' @export
+hardhat::extract_parameter_set_dials
+
+#' @importFrom hardhat extract_parameter_dials
+#' @export
+hardhat::extract_parameter_dials
+
+#' @importFrom dplyr %>%
+#' @export
+dplyr::`%>%`
diff --git a/man/adjust_prob_threshold.Rd b/man/adjust_prob_threshold.Rd
new file mode 100644
index 0000000..392f9c1
--- /dev/null
+++ b/man/adjust_prob_threshold.Rd
@@ -0,0 +1,36 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/prob_threshold.R
+\name{adjust_prob_threshold}
+\alias{adjust_prob_threshold}
+\title{Change the event threshold}
+\usage{
+adjust_prob_threshold(x, threshold = 0.5)
+}
+\arguments{
+\item{x}{A \code{\link[=container]{container()}}.}
+
+\item{threshold}{A numeric value (between zero and one) or \code{\link[hardhat:tune]{hardhat::tune()}}.}
+}
+\description{
+Change the event threshold
+}
+\examples{
+library(dplyr)
+library(modeldata)
+
+post_obj <-
+ container(mode = "classification") \%>\%
+ adjust_prob_threshold(threshold = .1)
+
+two_class_example \%>\% count(predicted)
+
+post_res <- fit(
+ post_obj,
+ two_class_example,
+ outcome = c(truth),
+ estimate = c(predicted),
+ probabilities = c(Class1, Class2)
+)
+
+predict(post_res, two_class_example) \%>\% count(predicted)
+}
diff --git a/man/container-package.Rd b/man/container-package.Rd
index 69fa1b8..8f314aa 100644
--- a/man/container-package.Rd
+++ b/man/container-package.Rd
@@ -2,7 +2,6 @@
% Please edit documentation in R/container-package.R
\docType{package}
\name{container-package}
-\alias{container}
\alias{container-package}
\title{container: Sandbox for a postprocessor object}
\description{
diff --git a/man/container.Rd b/man/container.Rd
new file mode 100644
index 0000000..e8aedef
--- /dev/null
+++ b/man/container.Rd
@@ -0,0 +1,39 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/container.R
+\name{container}
+\alias{container}
+\title{Declare post-processing for model predictions}
+\usage{
+container(
+ mode = "unknown",
+ type = "unknown",
+ outcome = character(0),
+ estimate = character(0),
+ probabilities = character(0),
+ time = character(0)
+)
+}
+\arguments{
+\item{mode}{The model's mode, one of \code{"unknown"}, \code{"classification"}, or
+\code{"regression"}. Modes of \code{"censored regression"} are not currently supported.}
+
+\item{type}{The model sub-type. Possible values are \code{"unknown"}, \code{"regression"},
+\code{"binary"}, or \code{"multiclass"}.}
+
+\item{outcome}{The name of the outcome variable.}
+
+\item{estimate}{The name of the point estimate (e.g. predicted class)}
+
+\item{probabilities}{The names of class probability estimates (if any). For
+classification, these should be given in the order of the factor levels of
+the \code{estimate}.}
+
+\item{time}{The name of the predicted event time.}
+}
+\description{
+Declare post-processing for model predictions
+}
+\examples{
+
+container()
+}
diff --git a/man/reexports.Rd b/man/reexports.Rd
new file mode 100644
index 0000000..43758ef
--- /dev/null
+++ b/man/reexports.Rd
@@ -0,0 +1,27 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/reexport.R
+\docType{import}
+\name{reexports}
+\alias{reexports}
+\alias{fit}
+\alias{tidy}
+\alias{required_pkgs}
+\alias{tunable}
+\alias{tune_args}
+\alias{extract_parameter_set_dials}
+\alias{extract_parameter_dials}
+\alias{\%>\%}
+\title{Objects exported from other packages}
+\keyword{internal}
+\description{
+These objects are imported from other packages. Follow the links
+below to see their documentation.
+
+\describe{
+ \item{dplyr}{\code{\link[dplyr:reexports]{\%>\%}}}
+
+ \item{generics}{\code{\link[generics]{fit}}, \code{\link[generics]{required_pkgs}}, \code{\link[generics]{tidy}}, \code{\link[generics]{tunable}}, \code{\link[generics]{tune_args}}}
+
+ \item{hardhat}{\code{\link[hardhat:hardhat-extract]{extract_parameter_dials}}, \code{\link[hardhat:hardhat-extract]{extract_parameter_set_dials}}}
+}}
+
diff --git a/tests/testthat.R b/tests/testthat.R
index 0dd3535..948d5ae 100644
--- a/tests/testthat.R
+++ b/tests/testthat.R
@@ -9,4 +9,4 @@
library(testthat)
library(container)
-test_check("container")
+# test_check("container")
From 3b4f35f87eb0393194d3d5e0b123d900aa81c672 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=98topepo=E2=80=99?= <‘mxkuhn@gmail.com’>
Date: Mon, 15 Apr 2024 13:47:00 -0400
Subject: [PATCH 02/39] type comments
---
R/operation.R | 21 +++++++++++++++++----
R/prob_threshold.R | 4 ++--
2 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/R/operation.R b/R/operation.R
index e9bbdc8..b0549d1 100644
--- a/R/operation.R
+++ b/R/operation.R
@@ -1,14 +1,27 @@
+# These values are used to specify "what will we need for the adjustment?" and
+# "what will we change?". For the outputs, we cannot change the probabilities
+# without changing the classes. This is important because we are going to have
+# to define constrains on the order of adjustments.
+
input_vals <- c("numeric", "probability", "class")
-output_vals <- c("numeric", "probability", "class")
+output_vals <- c("numeric", "probability_class", "class")
new_operation <- function(cls, inputs, outputs, arguments, results = list(trained = FALSE), ...) {
- # check outputs too: multi choice
- inputs <- rlang::arg_match0(inputs, input_vals)
+ inputs <- rlang::arg_match0(inputs, input_vals)
+ outputs <- rlang::arg_match0(outputs, output_vals)
+
+ if ( !any(names(results) == "trained") ){
+ cli::cli_abort("The {.arg results} slot requires a logical variable called \\
+ {.val trained}")
+ } else {
+ check_logical(results$trained)
+ }
+
res <-
list(
inputs = inputs,
- outputs = sort(outputs),
+ outputs = outputs,
arguments = arguments,
results = results
)
diff --git a/R/prob_threshold.R b/R/prob_threshold.R
index e448afe..b5fddb1 100644
--- a/R/prob_threshold.R
+++ b/R/prob_threshold.R
@@ -27,9 +27,9 @@ adjust_prob_threshold <- function(x, threshold = 0.5) {
new_operation(
"prob_threshold",
inputs = "probability",
- outputs = c("probability", "class"),
+ outputs = "class",
arguments = list(threshold = threshold),
- results = list()
+ results = list(trained = FALSE)
)
x$operations <- c(x$operations, list(op))
x
From 56ca8e52ace166d31ba45b59507d2bdd89b4940d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=98topepo=E2=80=99?= <‘mxkuhn@gmail.com’>
Date: Mon, 15 Apr 2024 13:56:05 -0400
Subject: [PATCH 03/39] is_tune
---
R/misc.R | 7 +++++++
R/prob_threshold.R | 14 +++++++++++---
2 files changed, 18 insertions(+), 3 deletions(-)
create mode 100644 R/misc.R
diff --git a/R/misc.R b/R/misc.R
new file mode 100644
index 0000000..e17ea8e
--- /dev/null
+++ b/R/misc.R
@@ -0,0 +1,7 @@
+is_tune <- function(x) {
+ if ( !is.call(x) ) {
+ return(FALSE)
+ }
+ isTRUE(identical(quote(tune), x[[1]]))
+}
+
diff --git a/R/prob_threshold.R b/R/prob_threshold.R
index b5fddb1..b24a44e 100644
--- a/R/prob_threshold.R
+++ b/R/prob_threshold.R
@@ -23,6 +23,11 @@
#' predict(post_res, two_class_example) %>% count(predicted)
#' @export
adjust_prob_threshold <- function(x, threshold = 0.5) {
+
+ if ( !is_tune(threshold) ) {
+ check_number_decimal(threshold, min = 10^-10, max = 1 - 10^-10)
+ }
+
op <-
new_operation(
"prob_threshold",
@@ -39,10 +44,13 @@ adjust_prob_threshold <- function(x, threshold = 0.5) {
print.prob_threshold <- function(x, ...) {
# check for tune() first
- trn <- ifelse(x$results$trained, " [trained]", "")
-
- cli::cli_inform(c("Adjust probability threshold to \\
+ if ( is_tune(x$arguments$threshold) ) {
+ cli::cli_inform("Adjust probability threshold to optimized value.")
+ } else {
+ trn <- ifelse(x$results$trained, " [trained]", "")
+ cli::cli_inform(c("Adjust probability threshold to \\
{signif(x$arguments$threshold, digits = 3)}{trn}"))
+ }
invisible(x)
}
From c1b173e90d8924a7b781057af93deaf968bbca35 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=98topepo=E2=80=99?= <‘mxkuhn@gmail.com’>
Date: Mon, 15 Apr 2024 15:50:22 -0400
Subject: [PATCH 04/39] numeric range constraints
---
R/numeric_range.R | 95 +++++++++++++++++++++++++++++++++++++
man/adjust_numeric_range.Rd | 17 +++++++
2 files changed, 112 insertions(+)
create mode 100644 R/numeric_range.R
create mode 100644 man/adjust_numeric_range.Rd
diff --git a/R/numeric_range.R b/R/numeric_range.R
new file mode 100644
index 0000000..5339efe
--- /dev/null
+++ b/R/numeric_range.R
@@ -0,0 +1,95 @@
+#' Truncate the range of numeric predictions
+#'
+#' @param x A [container()].
+#' @param upper_limit,lower_limit A numeric value, NA (for no truncation) or
+#' [hardhat::tune()].
+
+#' @export
+adjust_numeric_range <- function(x, lower_limit = -Inf, upper_limit = Inf) {
+
+ # input checks are done via probably::bound_prediction
+ op <-
+ new_operation(
+ "numeric_range",
+ inputs = "numeric",
+ outputs = "numeric",
+ arguments = list(lower_limit = lower_limit, upper_limit = upper_limit),
+ results = list(trained = FALSE)
+ )
+ x$operations <- c(x$operations, list(op))
+ x
+}
+
+#' @export
+print.numeric_range <- function(x, ...) {
+
+ # todo could be na
+ if ( !is_tune(x$arguments$lower_limit) ) {
+ if ( !is_tune(x$arguments$upper_limit) ) {
+ rng_txt <-
+ paste0(
+ "between [" ,
+ signif(x$arguments$lower_limit, 3),
+ ", ",
+ signif(x$arguments$upper_limit, 3),
+ "]"
+ )
+ } else {
+ rng_txt <- paste0("between [" , signif(x$arguments$lower_limit, 3), ", ?]")
+ }
+ } else {
+ if ( !is_tune(x$arguments$upper_limit) ) {
+ rng_txt <- paste0("between [?, ", signif(x$arguments$upper_limit, 3), "]")
+ } else {
+ rng_txt <- "between [?, ?]"
+ }
+
+ }
+
+ cli::cli_inform("Constrain numeric predictions to be {rng_txt}.")
+ invisible(x)
+}
+
+#' @export
+fit.numeric_range <- function(object, data, parent = NULL, ...) {
+ new_operation(
+ class(object),
+ inputs = object$inputs,
+ outputs = object$outputs,
+ arguments = object$arguments,
+ results = list(trained = TRUE)
+ )
+}
+
+#' @export
+predict.numeric_range <- function(object, new_data, parent, ...) {
+ est_nm <- parent$columns$estimate
+ lo <- object$arguments$lower_limit
+ hi <- object$arguments$upper_limit
+
+ # todo depends on tm predict col names
+ new_data[[ est_nm ]] <-
+ probably::bound_prediction(new_data, lower_limit = lo, upper_limit = hi)[[ est_nm ]]
+ new_data
+}
+
+#' @export
+required_pkgs.numeric_range <- function(x, ...) {
+ c("container", "probably")
+}
+
+#' @export
+tunable.numeric_range <- function(x, ...) {
+ tibble::tibble(
+ name = c("lower_limit", "upper_limit"),
+ call_info = list(list(pkg = "dials", fun = "lower_limit"), # todo make these dials functions
+ list(pkg = "dials", fun = "upper_limit")),
+ source = "container",
+ component = "numeric_range",
+ component_id = "numeric_range")
+}
+
+# todo missing methods:
+# todo tune_args
+# todo tidy
+# todo extract_parameter_set_dials
diff --git a/man/adjust_numeric_range.Rd b/man/adjust_numeric_range.Rd
new file mode 100644
index 0000000..4d3da9e
--- /dev/null
+++ b/man/adjust_numeric_range.Rd
@@ -0,0 +1,17 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/numeric_range.R
+\name{adjust_numeric_range}
+\alias{adjust_numeric_range}
+\title{Truncate the range of numeric predictions}
+\usage{
+adjust_numeric_range(x, lower_limit = -Inf, upper_limit = Inf)
+}
+\arguments{
+\item{x}{A \code{\link[=container]{container()}}.}
+
+\item{upper_limit, lower_limit}{A numeric value, NA (for no truncation) or
+\code{\link[hardhat:tune]{hardhat::tune()}}.}
+}
+\description{
+Truncate the range of numeric predictions
+}
From c62ac77113f54431d96e923701600b01498b766c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=98topepo=E2=80=99?= <‘mxkuhn@gmail.com’>
Date: Mon, 15 Apr 2024 15:50:36 -0400
Subject: [PATCH 05/39] numeric_calibration
---
NAMESPACE | 12 ++++++
R/misc.R | 10 +++++
R/numeric_calibration.R | 63 +++++++++++++++++++++++++++++++
man/adjust_numeric_calibration.Rd | 17 +++++++++
4 files changed, 102 insertions(+)
create mode 100644 R/numeric_calibration.R
create mode 100644 man/adjust_numeric_calibration.Rd
diff --git a/NAMESPACE b/NAMESPACE
index 8c38bfd..3204073 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -1,14 +1,26 @@
# Generated by roxygen2: do not edit by hand
S3method(fit,container)
+S3method(fit,numeric_calibration)
+S3method(fit,numeric_range)
S3method(fit,prob_threshold)
S3method(predict,container)
+S3method(predict,numeric_calibration)
+S3method(predict,numeric_range)
S3method(predict,prob_threshold)
S3method(print,container)
+S3method(print,numeric_calibration)
+S3method(print,numeric_range)
S3method(print,prob_threshold)
+S3method(required_pkgs,numeric_calibration)
+S3method(required_pkgs,numeric_range)
S3method(required_pkgs,prob_threshold)
+S3method(tunable,numeric_calibration)
+S3method(tunable,numeric_range)
S3method(tunable,prob_threshold)
export("%>%")
+export(adjust_numeric_calibration)
+export(adjust_numeric_range)
export(adjust_prob_threshold)
export(container)
export(extract_parameter_dials)
diff --git a/R/misc.R b/R/misc.R
index e17ea8e..e8b7a8e 100644
--- a/R/misc.R
+++ b/R/misc.R
@@ -5,3 +5,13 @@ is_tune <- function(x) {
isTRUE(identical(quote(tune), x[[1]]))
}
+# for operations with no tunable parameters
+
+no_param <-
+ tibble::tibble(
+ name = character(0),
+ call_info = list(),
+ source = character(0),
+ component = character(0),
+ component_id = character(0)
+ )
diff --git a/R/numeric_calibration.R b/R/numeric_calibration.R
new file mode 100644
index 0000000..0da1a14
--- /dev/null
+++ b/R/numeric_calibration.R
@@ -0,0 +1,63 @@
+#' Re-calibrate numeric predictions
+#'
+#' @param x A [container()].
+#' @param calibrator A pre-trained calibration method from the \pkg{probably}
+#' package, such as [probably::cal_estimate_linear()].
+#' @export
+adjust_numeric_calibration <- function(x, calibrator) {
+
+ if ( !inherits(calibrator, "cal_regression") ) {
+ cli::cli_abort("The {.arg calibrator} argument should be an object of //
+ class {.val 'cal_regression'}.")
+ }
+
+ op <-
+ new_operation(
+ "numeric_calibration",
+ inputs = "numeric",
+ outputs = "numeric",
+ arguments = list(calibrator = calibrator),
+ results = list(trained = FALSE)
+ )
+ x$operations <- c(x$operations, list(op))
+ x
+}
+
+#' @export
+print.numeric_calibration <- function(x, ...) {
+ trn <- ifelse(x$results$trained, " [trained]", "")
+ cli::cli_inform(c("Re-calibrate numeric predictions{trn}"))
+ invisible(x)
+}
+
+#' @export
+fit.numeric_calibration <- function(object, data, parent = NULL, ...) {
+ new_operation(
+ class(object),
+ inputs = object$inputs,
+ outputs = object$outputs,
+ arguments = object$arguments,
+ results = list(trained = TRUE)
+ )
+}
+
+#' @export
+predict.numeric_calibration <- function(object, new_data, parent, ...) {
+ probably::cal_apply(new_data, object$argument$calibrator)
+}
+
+# todo probably needs required_pkgs methods for cal objects
+#' @export
+required_pkgs.numeric_calibration <- function(x, ...) {
+ c("container", "probably")
+}
+
+#' @export
+tunable.numeric_calibration <- function (x, ...) {
+ no_param
+}
+
+# todo missing methods:
+# todo tune_args
+# todo tidy
+# todo extract_parameter_set_dials
diff --git a/man/adjust_numeric_calibration.Rd b/man/adjust_numeric_calibration.Rd
new file mode 100644
index 0000000..6a95b09
--- /dev/null
+++ b/man/adjust_numeric_calibration.Rd
@@ -0,0 +1,17 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/numeric_calibration.R
+\name{adjust_numeric_calibration}
+\alias{adjust_numeric_calibration}
+\title{Re-calibrate numeric predictions}
+\usage{
+adjust_numeric_calibration(x, calibrator)
+}
+\arguments{
+\item{x}{A \code{\link[=container]{container()}}.}
+
+\item{calibrator}{A pre-trained calibration method from the \pkg{probably}
+package, such as \code{\link[probably:cal_estimate_linear]{probably::cal_estimate_linear()}}.}
+}
+\description{
+Re-calibrate numeric predictions
+}
From 38b56620f48edd1f465f7196c859fa58dabaf11e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=98topepo=E2=80=99?= <‘mxkuhn@gmail.com’>
Date: Mon, 15 Apr 2024 15:50:49 -0400
Subject: [PATCH 06/39] add packages + tidy
---
DESCRIPTION | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/DESCRIPTION b/DESCRIPTION
index b04fd3d..1195534 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -6,17 +6,10 @@ Authors@R: c(
person("Hannah", "Frick", , "hannah@posit.co", role = "aut"),
person("Emil", "HvitFeldt", , "emil.hvitfeldt@posit.co", role = "aut"),
person("Max", "Kuhn", , "max@posit.co", role = c("aut", "cre")),
- person(given = "Posit Software, PBC", role = c("cph", "fnd"))
+ person("Posit Software, PBC", role = c("cph", "fnd"))
)
Description: Sandbox for a postprocessor object.
License: MIT + file LICENSE
-Suggests:
- modeldata,
- testthat (>= 3.0.0)
-Config/testthat/edition: 3
-Encoding: UTF-8
-Roxygen: list(markdown = TRUE)
-RoxygenNote: 7.3.1
URL: https://github.com/tidymodels/container
BugReports: https://github.com/tidymodels/container/issues
Imports:
@@ -24,7 +17,15 @@ Imports:
dplyr,
generics,
hardhat,
+ probably,
purrr,
rlang (>= 1.1.0),
tibble,
tidyselect
+Suggests:
+ modeldata,
+ testthat (>= 3.0.0)
+Config/testthat/edition: 3
+Encoding: UTF-8
+Roxygen: list(markdown = TRUE)
+RoxygenNote: 7.3.1
From 8f0b4a206dd4a463a0bc694f5b3226492380b989 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=98topepo=E2=80=99?= <‘mxkuhn@gmail.com’>
Date: Mon, 15 Apr 2024 15:51:04 -0400
Subject: [PATCH 07/39] auto-set type with reg mode
---
R/container.R | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/R/container.R b/R/container.R
index cbec12f..9719686 100644
--- a/R/container.R
+++ b/R/container.R
@@ -30,6 +30,11 @@ container <- function(mode = "unknown", type = "unknown", outcome = character(0)
new_container <- function(mode, type, operations, columns, ptype) {
mode <- rlang::arg_match0(mode, c("unknown", "regression", "classification", "censored regression"))
+
+ if ( mode == "regression" ) {
+ type <- "regression"
+ }
+
type <- rlang::arg_match0(type, c("unknown", "regression", "binary", "multiclass"))
if ( !is.list(operations) ) {
From 162c2124114183d8226c3aee508a20900d607d63 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=98topepo=E2=80=99?= <‘mxkuhn@gmail.com’>
Date: Mon, 15 Apr 2024 16:22:53 -0400
Subject: [PATCH 08/39] some example documentation
---
.../container_regression_example.html | 651 ++++++
.../examples/container_regression_example.qmd | 154 ++
.../figure-html/bad-pred-plot-1.png | Bin 0 -> 83748 bytes
.../figure-html/test-plot-1.png | Bin 0 -> 118044 bytes
.../libs/bootstrap/bootstrap-icons.css | 2078 +++++++++++++++++
.../libs/bootstrap/bootstrap-icons.woff | Bin 0 -> 176200 bytes
.../libs/bootstrap/bootstrap.min.css | 12 +
.../libs/bootstrap/bootstrap.min.js | 7 +
.../libs/clipboard/clipboard.min.js | 7 +
.../libs/quarto-html/anchor.min.js | 9 +
.../libs/quarto-html/popper.min.js | 6 +
.../quarto-syntax-highlighting.css | 203 ++
.../libs/quarto-html/quarto.js | 899 +++++++
.../libs/quarto-html/tippy.css | 1 +
.../libs/quarto-html/tippy.umd.min.js | 2 +
15 files changed, 4029 insertions(+)
create mode 100644 inst/examples/container_regression_example.html
create mode 100644 inst/examples/container_regression_example.qmd
create mode 100644 inst/examples/container_regression_example_files/figure-html/bad-pred-plot-1.png
create mode 100644 inst/examples/container_regression_example_files/figure-html/test-plot-1.png
create mode 100644 inst/examples/container_regression_example_files/libs/bootstrap/bootstrap-icons.css
create mode 100644 inst/examples/container_regression_example_files/libs/bootstrap/bootstrap-icons.woff
create mode 100644 inst/examples/container_regression_example_files/libs/bootstrap/bootstrap.min.css
create mode 100644 inst/examples/container_regression_example_files/libs/bootstrap/bootstrap.min.js
create mode 100644 inst/examples/container_regression_example_files/libs/clipboard/clipboard.min.js
create mode 100644 inst/examples/container_regression_example_files/libs/quarto-html/anchor.min.js
create mode 100644 inst/examples/container_regression_example_files/libs/quarto-html/popper.min.js
create mode 100644 inst/examples/container_regression_example_files/libs/quarto-html/quarto-syntax-highlighting.css
create mode 100644 inst/examples/container_regression_example_files/libs/quarto-html/quarto.js
create mode 100644 inst/examples/container_regression_example_files/libs/quarto-html/tippy.css
create mode 100644 inst/examples/container_regression_example_files/libs/quarto-html/tippy.umd.min.js
diff --git a/inst/examples/container_regression_example.html b/inst/examples/container_regression_example.html
new file mode 100644
index 0000000..8fb4c83
--- /dev/null
+++ b/inst/examples/container_regression_example.html
@@ -0,0 +1,651 @@
+
+
+
+
+
+
+
+
+
+container regression example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+This is an example regression analysis to show how the container package might work.
+We’ll use the food delivery data and start with a three-way split:
+
+
library (tidymodels)
+library (bonsai) # also requires lightgbm package
+library (container) # pak::pak(c("tidymodels/container@max"), ask = FALSE)
+library (probably)
+library (patchwork)
+
+# ------------------------------------------------------------------------------
+
+tidymodels_prefer ()
+theme_set (theme_bw ())
+options (pillar.advice = FALSE , pillar.min_title_chars = Inf )
+
+# ------------------------------------------------------------------------------
+
+data (deliveries, package = "modeldata" )
+
+set.seed (991 )
+ delivery_split <- initial_validation_split (deliveries, prop = c (0.6 , 0.2 ),
+ strata = time_to_delivery)
+ delivery_train <- training (delivery_split)
+ delivery_test <- testing (delivery_split)
+ delivery_val <- validation (delivery_split)
+
+Let’s deliberately fit a regression model that has poor predicted values: a boosted tree with only three ensemble members:
+
+
bst_fit <-
+ boost_tree (trees = 3 ) %>%
+ set_engine ("lightgbm" ) %>%
+ set_mode ("regression" ) %>%
+ fit (time_to_delivery ~ ., data = delivery_train)
+
+We predict the validation set and see how bad things are:
+
+
reg_metrics <- metric_set (rmse, rsq)
+
+ bst_val_pred <- augment (bst_fit, delivery_val)
+reg_metrics (bst_val_pred, truth = time_to_delivery, estimate = .pred)
+
+
# A tibble: 2 × 3
+ .metric .estimator .estimate
+ <chr> <chr> <dbl>
+1 rmse standard 5.46
+2 rsq standard 0.850
+
+
+That R2 looks great ! How well is it calibrated?
+
+
cal_plot_regression (bst_val_pred, truth = time_to_delivery, estimate = .pred)
+
+
+
+
+
+
+
+
+Ooof. One of the calibration tools for the probably package might help this. Let’s use a linear regression with spline terms to fix it. First, we’ll resample the calibration model to see if it helps:
+
+
set.seed (10 )
+ bst_val_pred %>%
+ vfold_cv () %>%
+ cal_validate_linear (truth = time_to_delivery, estimate = .pred,
+ smooth = TRUE , metrics = reg_metrics) %>%
+ collect_metrics ()
+
+
# A tibble: 4 × 7
+ .metric .type .estimator mean n std_err .config
+ <chr> <chr> <chr> <dbl> <int> <dbl> <chr>
+1 rmse uncalibrated standard 5.45 10 0.122 config
+2 rsq uncalibrated standard 0.851 10 0.0133 config
+3 rmse calibrated standard 2.69 10 0.125 config
+4 rsq calibrated standard 0.851 10 0.0133 config
+
+
+That seems promising. Let’s fit it to the validation set predictions:
+
+
bst_cal <- cal_estimate_linear (bst_val_pred, truth = time_to_delivery,
+ estimate = .pred, smooth = TRUE )
+
+We could manually use cal_apply()
to adjust predictions, but instead, we’ll add it to the post-processing object:
+
+
post_obj <-
+ container (mode = "regression" ) %>%
+ adjust_numeric_calibration (bst_cal)
+ post_obj
+
+
regression post-processing object with 1 operation
+
+
+
Re-calibrate numeric predictions
+
+
+Let’s add another post-processor to limit the range of predictions (just as a demonstration):
+
+
post_obj <-
+ post_obj %>%
+ adjust_numeric_range (lower_limit = 0 , upper_limit = 50 )
+ post_obj
+
+
regression post-processing object with 2 operations
+
+
+
Re-calibrate numeric predictions
+Constrain numeric predictions to be between [0, 50].
+
+
+We have to fit the post-processor to use it. However, there are no estimation steps in this instance since everything is either pre-trained (e.g., the calibrator) or user-defined (e.g., the limits). We’ll run fit()
anyway, then apply it to the test results:
+
+
post_res <-
+ post_obj %>%
+ fit (bst_val_pred, outcome = c (time_to_delivery), estimate = c (.pred))
+
+ bst_test_pred <- augment (bst_fit, delivery_test)
+
+# Without:
+reg_metrics (bst_test_pred, truth = time_to_delivery, estimate = .pred)
+
+
# A tibble: 2 × 3
+ .metric .estimator .estimate
+ <chr> <chr> <dbl>
+1 rmse standard 5.14
+2 rsq standard 0.848
+
+
# With:
+ bst_test_proc_pred <-
+ post_res %>%
+ predict (bst_test_pred)
+
+ bst_test_proc_pred %>%
+ reg_metrics (truth = time_to_delivery, estimate = .pred)
+
+
# A tibble: 2 × 3
+ .metric .estimator .estimate
+ <chr> <chr> <dbl>
+1 rmse standard 2.61
+2 rsq standard 0.848
+
+
+Visually:
+
+
before <- cal_plot_regression (bst_test_pred, truth = time_to_delivery,
+ estimate = .pred)
+ after <- cal_plot_regression (bst_test_proc_pred, truth = time_to_delivery,
+ estimate = .pred)
+
+ before + after
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/inst/examples/container_regression_example.qmd b/inst/examples/container_regression_example.qmd
new file mode 100644
index 0000000..bebdb00
--- /dev/null
+++ b/inst/examples/container_regression_example.qmd
@@ -0,0 +1,154 @@
+---
+title: "container regression example"
+---
+
+This is an example regression analysis to show how the container package might work.
+
+We'll use the [food delivery data](https://aml4td.org/chapters/whole-game.html) and start with a three-way split:
+
+```{r}
+#| label: ssshhh
+#| include: false
+
+library(tidymodels)
+library(bonsai)
+library(container)
+library(probably)
+library(patchwork)
+```
+```{r}
+#| label: startup
+library(tidymodels)
+library(bonsai) # also requires lightgbm package
+library(container) # pak::pak(c("tidymodels/container@max"), ask = FALSE)
+library(probably)
+library(patchwork)
+
+# ------------------------------------------------------------------------------
+
+tidymodels_prefer()
+theme_set(theme_bw())
+options(pillar.advice = FALSE, pillar.min_title_chars = Inf)
+
+# ------------------------------------------------------------------------------
+
+data(deliveries, package = "modeldata")
+
+set.seed(991)
+delivery_split <- initial_validation_split(deliveries, prop = c(0.6, 0.2),
+ strata = time_to_delivery)
+delivery_train <- training(delivery_split)
+delivery_test <- testing(delivery_split)
+delivery_val <- validation(delivery_split)
+```
+
+Let's deliberately fit a regression model that has poor predicted values: a boosted tree with only three ensemble members:
+
+```{r}
+#| label: bad-boost
+
+bst_fit <-
+ boost_tree(trees = 3) %>%
+ set_engine("lightgbm") %>%
+ set_mode("regression") %>%
+ fit(time_to_delivery ~ ., data = delivery_train)
+```
+
+We predict the validation set and see how bad things are:
+
+```{r}
+#| label: bad-pred
+
+reg_metrics <- metric_set(rmse, rsq)
+
+bst_val_pred <- augment(bst_fit, delivery_val)
+reg_metrics(bst_val_pred, truth = time_to_delivery, estimate = .pred)
+```
+
+That R2 looks _great_! How well is it calibrated?
+
+```{r}
+#| label: bad-pred-plot
+cal_plot_regression(bst_val_pred, truth = time_to_delivery, estimate = .pred)
+```
+
+
+Ooof. One of the calibration tools for the probably package might help this. Let's use a linear regression with spline terms to fix it. First, we'll resample the calibration model to see if it helps:
+
+```{r}
+#| label: cal-resample
+
+set.seed(10)
+bst_val_pred %>%
+ vfold_cv() %>%
+ cal_validate_linear(truth = time_to_delivery, estimate = .pred,
+ smooth = TRUE, metrics = reg_metrics) %>%
+ collect_metrics()
+```
+
+That seems promising. Let's fit it to the validation set predictions:
+
+```{r}
+#| label: cal-obj
+
+bst_cal <- cal_estimate_linear(bst_val_pred, truth = time_to_delivery,
+ estimate = .pred, smooth = TRUE)
+```
+
+We could manually use `cal_apply()` to adjust predictions, but instead, we'll add it to the post-processing object:
+
+```{r}
+#| label: post-1
+
+post_obj <-
+ container(mode = "regression") %>%
+ adjust_numeric_calibration(bst_cal)
+post_obj
+```
+
+Let's add another post-processor to limit the range of predictions (just as a demonstration):
+
+```{r}
+#| label: post-2
+
+post_obj <-
+ post_obj %>%
+ adjust_numeric_range(lower_limit = 0, upper_limit = 50)
+post_obj
+```
+
+We have to fit the post-processor to use it. However, there are no estimation steps in this instance since everything is either pre-trained (e.g., the calibrator) or user-defined (e.g., the limits). We'll run `fit()` anyway, then apply it to the test results:
+
+```{r}
+#| label: test-pred
+
+post_res <-
+ post_obj %>%
+ fit(bst_val_pred, outcome = c(time_to_delivery), estimate = c(.pred))
+
+bst_test_pred <- augment(bst_fit, delivery_test)
+
+# Without:
+reg_metrics(bst_test_pred, truth = time_to_delivery, estimate = .pred)
+
+# With:
+bst_test_proc_pred <-
+ post_res %>%
+ predict(bst_test_pred)
+
+bst_test_proc_pred %>%
+ reg_metrics(truth = time_to_delivery, estimate = .pred)
+```
+
+Visually:
+
+```{r}
+#| label: test-plot
+
+before <- cal_plot_regression(bst_test_pred, truth = time_to_delivery,
+ estimate = .pred)
+after <- cal_plot_regression(bst_test_proc_pred, truth = time_to_delivery,
+ estimate = .pred)
+
+before + after
+```
diff --git a/inst/examples/container_regression_example_files/figure-html/bad-pred-plot-1.png b/inst/examples/container_regression_example_files/figure-html/bad-pred-plot-1.png
new file mode 100644
index 0000000000000000000000000000000000000000..d64b6556168f5537d993fadfd27399671bda8181
GIT binary patch
literal 83748
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