diff --git a/DESCRIPTION b/DESCRIPTION index 4e59523..08959d6 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: probably Title: Tools for Post-Processing Predicted Values -Version: 1.1.1.9000 +Version: 1.1.1.9001 Authors@R: c( person("Max", "Kuhn", , "max@posit.co", role = c("aut", "cre")), person("Davis", "Vaughan", , "davis@posit.co", role = "aut"), diff --git a/NAMESPACE b/NAMESPACE index 23ed2dd..de28cd6 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -126,6 +126,10 @@ S3method(required_pkgs,cal_estimate_multinomial) S3method(required_pkgs,cal_estimate_multinomial_spline) S3method(required_pkgs,cal_estimate_none) S3method(required_pkgs,cal_object) +S3method(required_pkgs,int_conformal_cv) +S3method(required_pkgs,int_conformal_full) +S3method(required_pkgs,int_conformal_quantile) +S3method(required_pkgs,int_conformal_split) S3method(threshold_perf,data.frame) S3method(type_sum,cal_object) S3method(vec_cast,character.class_pred) diff --git a/NEWS.md b/NEWS.md index e788116..7be95a0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # probably (development version) +* Add `required_pkgs()` methods to `int_conformal_cv()`, `int_conformal_full()`, `int_conformal_quantile()`, and `int_conformal_split()`. (#190) + # probably 1.1.1 * Updated unit tests for new ggplot2 release (#180). diff --git a/R/conformal_infer.R b/R/conformal_infer.R index 10fadfa..6511193 100644 --- a/R/conformal_infer.R +++ b/R/conformal_infer.R @@ -113,6 +113,20 @@ print.int_conformal_full <- function(x, ...) { invisible(x) } +#' @rdname required_pkgs.int_conformal_cv +#' @keywords internal +#' @export +required_pkgs.int_conformal_full <- function(x, infra = TRUE, ...) { + model_pkgs <- required_pkgs(x$wflow, infra = infra) + + if (infra) { + model_pkgs <- c(model_pkgs, "probably") + } + + model_pkgs <- unique(model_pkgs) + model_pkgs +} + # ------------------------------------------------------------------------------ #' Prediction intervals from conformal methods diff --git a/R/conformal_infer_cv.R b/R/conformal_infer_cv.R index 41a774c..79c2ebb 100644 --- a/R/conformal_infer_cv.R +++ b/R/conformal_infer_cv.R @@ -148,6 +148,23 @@ print.int_conformal_cv <- function(x, ...) { invisible(x) } +#' S3 methods to track which additional packages are needed for prediction +#' intervals via conformal inference +#' @param x a conformal interval object +#' @inheritParams generics::required_pkgs +#' @export +required_pkgs.int_conformal_cv <- function(x, infra = TRUE, ...) { + model_pkgs <- map(x$models, required_pkgs, infra = infra) + model_pkgs <- unlist(model_pkgs) + + if (infra) { + model_pkgs <- c(model_pkgs, "probably") + } + + model_pkgs <- unique(model_pkgs) + model_pkgs +} + # ------------------------------------------------------------------------------ # helpers diff --git a/R/conformal_infer_quantile.R b/R/conformal_infer_quantile.R index c2227b5..3f7803c 100644 --- a/R/conformal_infer_quantile.R +++ b/R/conformal_infer_quantile.R @@ -115,6 +115,20 @@ print.int_conformal_quantile <- function(x, ...) { invisible(x) } +#' @rdname required_pkgs.int_conformal_cv +#' @keywords internal +#' @export +required_pkgs.int_conformal_quantile <- function(x, infra = TRUE, ...) { + model_pkgs <- required_pkgs(x$wflow, infra = infra) + + if (infra) { + model_pkgs <- c(model_pkgs, "probably") + } + + model_pkgs <- unique(model_pkgs) + model_pkgs +} + #' @export #' @rdname predict.int_conformal_full predict.int_conformal_quantile <- function(object, new_data, ...) { diff --git a/R/conformal_infer_split.R b/R/conformal_infer_split.R index ddcfb4a..a8f38c3 100644 --- a/R/conformal_infer_split.R +++ b/R/conformal_infer_split.R @@ -94,6 +94,20 @@ print.int_conformal_split <- function(x, ...) { invisible(x) } +#' @rdname required_pkgs.int_conformal_cv +#' @keywords internal +#' @export +required_pkgs.int_conformal_split <- function(x, infra = TRUE, ...) { + model_pkgs <- required_pkgs(x$wflow, infra = infra) + + if (infra) { + model_pkgs <- c(model_pkgs, "probably") + } + + model_pkgs <- unique(model_pkgs) + model_pkgs +} + #' @export #' @rdname predict.int_conformal_full predict.int_conformal_split <- function(object, new_data, level = 0.95, ...) { diff --git a/man/int_conformal_full.Rd b/man/int_conformal_full.Rd index 09faf20..e950fe7 100644 --- a/man/int_conformal_full.Rd +++ b/man/int_conformal_full.Rd @@ -68,6 +68,77 @@ the initial set of training set residuals are modeled using a Gamma generalized linear model with a log link (see the reference by Aitkin below). For a new sample, the absolute size of the residual is estimated and a multiple of this value is computed as an initial guess of the search boundaries. + +\subsection{Speed}{ + +The time it takes to compute the intervals depends on the training set +size, search parameters (i.e., convergence criterion, number of +iterations), the grid size, and the number of worker processes that are +used. For the last item, the computations can be parallelized using the +future and furrr packages. + +To use parallelism, the \code{\link[future:plan]{future::plan()}} function can +be invoked to create a parallel backend. For example, let’s make an +initial workflow: + +\if{html}{\out{
}}\preformatted{library(tidymodels) +library(probably) +library(future) + +tidymodels_prefer() + +## Make a fitted workflow from some simulated data: +set.seed(121) +train_dat <- sim_regression(200) +new_dat <- sim_regression( 5) |> select(-outcome) + +lm_fit <- + workflow() |> + add_model(linear_reg()) |> + add_formula(outcome ~ .) |> + fit(data = train_dat) + +# Create the object to be used to make prediction intervals +lm_conform <- int_conformal_full(lm_fit, train_dat) +}\if{html}{\out{
}} + +We’ll use a \code{"multisession"} parallel processing plan to compute the +intervals for the five new samples in parallel: + +\if{html}{\out{
}}\preformatted{plan("multisession") + +# This is run in parallel: +predict(lm_conform, new_dat) +}\if{html}{\out{
}} + +\if{html}{\out{
}}\preformatted{## # A tibble: 5 x 2 +## .pred_lower .pred_upper +## +## 1 -17.9 59.6 +## 2 -33.7 51.1 +## 3 -30.6 48.2 +## 4 -17.3 59.6 +## 5 -23.3 55.2 +}\if{html}{\out{
}} + +Using simulations, there are slightly sub-linear speed-ups when using +parallel processing to compute the row-wise intervals. + +In comparison with parametric intervals: + +\if{html}{\out{
}}\preformatted{predict(lm_fit, new_dat, type = "pred_int") +}\if{html}{\out{
}} + +\if{html}{\out{
}}\preformatted{## # A tibble: 5 x 2 +## .pred_lower .pred_upper +## +## 1 -19.2 59.1 +## 2 -31.8 49.7 +## 3 -31.0 47.6 +## 4 -17.8 60.1 +## 5 -23.6 54.3 +}\if{html}{\out{
}} +} } \references{ Jing Lei, Max G'Sell, Alessandro Rinaldo, Ryan J. Tibshirani and Larry diff --git a/man/required_pkgs.int_conformal_cv.Rd b/man/required_pkgs.int_conformal_cv.Rd new file mode 100644 index 0000000..e5d2d84 --- /dev/null +++ b/man/required_pkgs.int_conformal_cv.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/conformal_infer.R, R/conformal_infer_cv.R, +% R/conformal_infer_quantile.R, R/conformal_infer_split.R +\name{required_pkgs.int_conformal_full} +\alias{required_pkgs.int_conformal_full} +\alias{required_pkgs.int_conformal_cv} +\alias{required_pkgs.int_conformal_quantile} +\alias{required_pkgs.int_conformal_split} +\title{S3 methods to track which additional packages are needed for prediction +intervals via conformal inference} +\usage{ +\method{required_pkgs}{int_conformal_full}(x, infra = TRUE, ...) + +\method{required_pkgs}{int_conformal_cv}(x, infra = TRUE, ...) + +\method{required_pkgs}{int_conformal_quantile}(x, infra = TRUE, ...) + +\method{required_pkgs}{int_conformal_split}(x, infra = TRUE, ...) +} +\arguments{ +\item{x}{a conformal interval object} + +\item{...}{Other arguments passed to methods} +} +\description{ +S3 methods to track which additional packages are needed for prediction +intervals via conformal inference +} +\keyword{internal} diff --git a/tests/testthat/test-conformal-intervals-quantile.R b/tests/testthat/test-conformal-intervals-quantile.R index c11102f..293c773 100644 --- a/tests/testthat/test-conformal-intervals-quantile.R +++ b/tests/testthat/test-conformal-intervals-quantile.R @@ -63,4 +63,13 @@ test_that("split conformal quantile intervals", { nrow(new_int), nrow(sim_new) ) + + expect_identical( + required_pkgs(lm_int), + c(required_pkgs(lm_int$wflow), "probably") + ) + expect_identical( + required_pkgs(lm_int, infra = FALSE), + required_pkgs(lm_int$wflow, infra = FALSE) + ) }) diff --git a/tests/testthat/test-conformal-intervals-split.R b/tests/testthat/test-conformal-intervals-split.R index 280440f..6824e4e 100644 --- a/tests/testthat/test-conformal-intervals-split.R +++ b/tests/testthat/test-conformal-intervals-split.R @@ -64,4 +64,13 @@ test_that("split conformal intervals", { nrow(new_int), nrow(sim_new) ) + + expect_identical( + required_pkgs(lm_int), + c(required_pkgs(lm_int$wflow), "probably") + ) + expect_identical( + required_pkgs(lm_int, infra = FALSE), + required_pkgs(lm_int$wflow, infra = FALSE) + ) }) diff --git a/tests/testthat/test-conformal-intervals.R b/tests/testthat/test-conformal-intervals.R index 467fada..5233d2e 100644 --- a/tests/testthat/test-conformal-intervals.R +++ b/tests/testthat/test-conformal-intervals.R @@ -229,6 +229,24 @@ test_that("conformal intervals", { expect_true( all(grid_bounds$.pred_lower < grid_bounds_90$.pred_lower) ) + + expect_identical( + required_pkgs(smol_obj), + c(required_pkgs(smol_obj$wflow), "probably") + ) + expect_identical( + required_pkgs(smol_obj, infra = FALSE), + required_pkgs(smol_obj$wflow, infra = FALSE) + ) + + expect_identical( + required_pkgs(grid_int), + c(unique(unlist(map(grid_int$models, required_pkgs))), "probably") + ) + expect_identical( + required_pkgs(grid_int, infra = FALSE), + unique(unlist(map(grid_int$models, required_pkgs, infra = FALSE))) + ) }) test_that("conformal control", {