diff --git a/DESCRIPTION b/DESCRIPTION index 938a487..1bd0629 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -38,7 +38,7 @@ Imports: yardstick Suggests: derivoce, - fancygam, + fancyfx, knitr, leaflet, ranger, @@ -55,7 +55,7 @@ Suggests: Remotes: chross22/datamatch, chross22/derivoce, - chross22/fancygam + chross22/fancyfx Config/testthat/edition: 3 VignetteBuilder: knitr URL: https://github.com/chross22/taupatch diff --git a/R/plot_effects.R b/R/plot_effects.R index 5d2eaf2..0997a89 100644 --- a/R/plot_effects.R +++ b/R/plot_effects.R @@ -188,7 +188,7 @@ plot_glm_coefficients <- function(coefficients, path = NULL) { #' The `ranger`, `xgb.Booster`, `glm`, or `gam` object inside the workflow, #' rather than the workflow wrapping it. Anything that plots or interrogates a #' model directly needs this rather than the tidymodels object — including -#' [fancygam](https://github.com/chross22/fancygam), whose `plotSmooths()` takes +#' [fancyfx](https://github.com/chross22/fancyfx), whose `plotEffects()` takes #' an `mgcv` fit, which is what this returns for `model.type: gam`. #' #' @param model a fitted model from [fit_patch_model()], or a fitted workflow @@ -196,7 +196,7 @@ plot_glm_coefficients <- function(coefficients, path = NULL) { #' @examples #' \dontrun{ #' # Prettier smooths than the generic partial effects can give: -#' fancygam::plotSmooths(model_engine_fit(model)) +#' fancyfx::plotEffects(model_engine_fit(model), dat, "SST") #' } #' @export model_engine_fit <- function(model) { @@ -239,16 +239,28 @@ gam_smooth_terms <- function(fitted) { out[order(-out$edf), ] } -#' Whether fancygam is available to draw smooths +#' Whether fancyfx is available to draw smooths #' #' Its own function so the optional path can be exercised in tests without #' mocking `requireNamespace()` itself, which every package that loads a #' graphics device also goes through. #' -#' @return `TRUE` when fancygam is installed +#' @section It used to be called fancygam: +#' The package was renamed when it grew past GAMs. The rename is why this +#' matters more than a find-and-replace: `chross22/fancygam` still resolves on +#' GitHub, so `Remotes: chross22/fancygam` kept installing — but what it +#' installs now declares `Package: fancyfx`, so `requireNamespace("fancygam")` +#' returned `FALSE` on every fresh install and the smooth plots were skipped in +#' silence. Anyone with the old package still sitting in their library saw +#' nothing wrong. +#' +#' That is the failure mode to watch for here: this function gates a diagnostic +#' rather than the run, so a wrong answer costs a plot and no error. +#' +#' @return `TRUE` when fancyfx is installed #' @keywords internal -has_fancygam <- function() { - requireNamespace("fancygam", quietly = TRUE) +has_fancyfx <- function() { + requireNamespace("fancyfx", quietly = TRUE) } #' Variables a fitted GAM gave a smooth to @@ -267,7 +279,7 @@ gam_smoothed_variables <- function(fitted) { sub("^s\\((.*)\\)$", "\\1", terms) } -#' Plot a GAM's fitted smooths, with fancygam +#' Plot a GAM's fitted smooths, with fancyfx #' #' The partial effect of each smooth term on the log-odds scale, with its #' standard error band and a rug showing where the data actually is. This is the @@ -275,7 +287,7 @@ gam_smoothed_variables <- function(fitted) { #' rather than reconstructed by prediction, so it carries uncertainty, which a #' partial dependence curve cannot. #' -#' Drawn by [fancygam](https://github.com/chross22/fancygam), which is a Suggests +#' Drawn by [fancyfx](https://github.com/chross22/fancyfx), which is a Suggests #' — a run without it still gets the generic partial effect curves. #' #' @section Why the axes read in standard deviations: @@ -297,9 +309,9 @@ gam_smoothed_variables <- function(fitted) { #' @seealso [gam_smooth_terms()] for the numbers behind these #' @export plot_gam_smooths <- function(model, vars = NULL, path = NULL) { - if (!has_fancygam()) { - stop("The 'fancygam' package is required to plot GAM smooths. ", - "Install it with remotes::install_github('chross22/fancygam').", + if (!has_fancyfx()) { + stop("The 'fancyfx' package is required to plot GAM smooths. ", + "Install it with remotes::install_github('chross22/fancyfx').", call. = FALSE) } workflow <- if (inherits(model, "workflow")) model else model$workflow @@ -314,7 +326,7 @@ plot_gam_smooths <- function(model, vars = NULL, path = NULL) { # would be drawn on different x scales and quietly disagree. baked <- as.data.frame(workflows::extract_mold(workflow)$predictors) - plot <- fancygam::combinePlots(model_engine_fit(workflow), baked, vars) + plot <- fancyfx::combinePlots(model_engine_fit(workflow), baked, vars) if (is.null(path)) return(plot) ggplot2::ggsave(path, plot = plot, dpi = 150, @@ -371,9 +383,9 @@ write_effect_plots <- function(model, out) { } # The fitted smooths themselves, with their uncertainty. Skipped without a - # word when fancygam is absent: it is a Suggests, and the generic partial + # word when fancyfx is absent: it is a Suggests, and the generic partial # effect curves above already cover the question. - if (has_fancygam()) { + if (has_fancyfx()) { smooths <- try_diagnostic( plot_gam_smooths(model, path = file.path(out, "gam_smooths.png")), "GAM smooth plots" diff --git a/README.md b/README.md index b6868e0..da86acf 100644 --- a/README.md +++ b/README.md @@ -877,13 +877,13 @@ On top of that, each model contributes what only it can: | `gam` | Effective degrees of freedom per smooth. An `edf` of 1 means the smooth collapsed to a line | | `rf` / `brt` | None. The partial effect curve *is* their answer | -With [`fancygam`](https://github.com/chross22/fancygam) installed, a GAM also +With [`fancyfx`](https://github.com/chross22/fancyfx) installed, a GAM also gets its **fitted smooths** drawn — each term with its standard error band and a rug showing where the data actually is. Those carry uncertainty, which a partial dependence curve cannot: ```r -remotes::install_github("chross22/fancygam") +remotes::install_github("chross22/fancyfx") ``` Their x axes read in standard deviations, because the smooths belong to the model @@ -1182,7 +1182,7 @@ diagnostics/cv_predictions.csv held-out predictions, for any metric not tabu diagnostics/partial_effects.png what each predictor does to patch probability diagnostics/coefficients.png glm only: signed effects with intervals diagnostics/smooth_terms.csv gam only: effective degrees of freedom per smooth -diagnostics/gam_smooths.png gam only, with fancygam: fitted smooths with error bands +diagnostics/gam_smooths.png gam only, with fancyfx: fitted smooths with error bands diagnostics/members// with model.ensemble: the above, one directory per algorithm projections/suitability.csv every cell of every month: species, year, month, lon, lat, probability plus the interval and novelty columns, with projection.uncertainty diff --git a/inst/shiny/app.R b/inst/shiny/app.R index ed882ad..ac3e084 100644 --- a/inst/shiny/app.R +++ b/inst/shiny/app.R @@ -1210,16 +1210,16 @@ server <- function(input, output, session) { # A GAM has its own partial effects, and they are better than the generic # ones: read out of the fitted model rather than reconstructed by prediction, # so they carry the uncertainty a partial dependence curve cannot. - use_fancygam <- reactive({ + use_fancyfx <- reactive({ identical(run_result()$model$type, "gam") && - requireNamespace("fancygam", quietly = TRUE) + requireNamespace("fancyfx", quietly = TRUE) }) output$partial_effects_note <- renderUI({ req(run_result()) - if (isTRUE(use_fancygam())) { + if (isTRUE(use_fancyfx())) { return(helpText("The model's own smooths, with standard error bands and a", - "rug showing where the data is, drawn by fancygam. The x", + "rug showing where the data is, drawn by fancyfx. The x", "axes are in standard deviations because the model was", "fitted on the centred and scaled predictors - turn off", "'Centre and scale' to read them in the covariate's own", @@ -1233,7 +1233,7 @@ server <- function(input, output, session) { output$partial_effects <- renderPlot({ req(run_result()) - if (isTRUE(use_fancygam())) { + if (isTRUE(use_fancyfx())) { return(suppressMessages(plot_gam_smooths(run_result()$model))) } effects <- run_effects() @@ -1257,7 +1257,7 @@ server <- function(input, output, session) { } if (identical(model$type, "gam")) { # The smooths themselves are the partial effects panel above when - # fancygam is present, so they are not repeated here. + # fancyfx is present, so they are not repeated here. return(tagList( h4("Smooth terms"), helpText("Effective degrees of freedom per smooth. An edf of 1 means", diff --git a/man/has_fancyfx.Rd b/man/has_fancyfx.Rd new file mode 100644 index 0000000..a35d08d --- /dev/null +++ b/man/has_fancyfx.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/plot_effects.R +\name{has_fancyfx} +\alias{has_fancyfx} +\title{Whether fancyfx is available to draw smooths} +\usage{ +has_fancyfx() +} +\value{ +\code{TRUE} when fancyfx is installed +} +\description{ +Its own function so the optional path can be exercised in tests without +mocking \code{requireNamespace()} itself, which every package that loads a +graphics device also goes through. +} +\section{It used to be called fancygam}{ + +The package was renamed when it grew past GAMs. The rename is why this +matters more than a find-and-replace: \code{chross22/fancygam} still resolves on +GitHub, so \code{Remotes: chross22/fancygam} kept installing — but what it +installs now declares \code{Package: fancyfx}, so \code{requireNamespace("fancygam")} +returned \code{FALSE} on every fresh install and the smooth plots were skipped in +silence. Anyone with the old package still sitting in their library saw +nothing wrong. + +That is the failure mode to watch for here: this function gates a diagnostic +rather than the run, so a wrong answer costs a plot and no error. +} + +\keyword{internal} diff --git a/man/has_fancygam.Rd b/man/has_fancygam.Rd deleted file mode 100644 index f28451e..0000000 --- a/man/has_fancygam.Rd +++ /dev/null @@ -1,17 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/plot_effects.R -\name{has_fancygam} -\alias{has_fancygam} -\title{Whether fancygam is available to draw smooths} -\usage{ -has_fancygam() -} -\value{ -\code{TRUE} when fancygam is installed -} -\description{ -Its own function so the optional path can be exercised in tests without -mocking \code{requireNamespace()} itself, which every package that loads a -graphics device also goes through. -} -\keyword{internal} diff --git a/man/model_engine_fit.Rd b/man/model_engine_fit.Rd index 6439a70..5912a0d 100644 --- a/man/model_engine_fit.Rd +++ b/man/model_engine_fit.Rd @@ -16,12 +16,12 @@ the engine's own fitted object The \code{ranger}, \code{xgb.Booster}, \code{glm}, or \code{gam} object inside the workflow, rather than the workflow wrapping it. Anything that plots or interrogates a model directly needs this rather than the tidymodels object — including -\href{https://github.com/chross22/fancygam}{fancygam}, whose \code{plotSmooths()} takes +\href{https://github.com/chross22/fancyfx}{fancyfx}, whose \code{plotEffects()} takes an \code{mgcv} fit, which is what this returns for \code{model.type: gam}. } \examples{ \dontrun{ # Prettier smooths than the generic partial effects can give: -fancygam::plotSmooths(model_engine_fit(model)) +fancyfx::plotEffects(model_engine_fit(model), dat, "SST") } } diff --git a/man/plot_gam_smooths.Rd b/man/plot_gam_smooths.Rd index 1f8ddde..e0831bb 100644 --- a/man/plot_gam_smooths.Rd +++ b/man/plot_gam_smooths.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/plot_effects.R \name{plot_gam_smooths} \alias{plot_gam_smooths} -\title{Plot a GAM's fitted smooths, with fancygam} +\title{Plot a GAM's fitted smooths, with fancyfx} \usage{ plot_gam_smooths(model, vars = NULL, path = NULL) } @@ -24,7 +24,7 @@ rather than reconstructed by prediction, so it carries uncertainty, which a partial dependence curve cannot. } \details{ -Drawn by \href{https://github.com/chross22/fancygam}{fancygam}, which is a Suggests +Drawn by \href{https://github.com/chross22/fancyfx}{fancyfx}, which is a Suggests — a run without it still gets the generic partial effect curves. } \section{Why the axes read in standard deviations}{ diff --git a/tests/testthat/test-effects.R b/tests/testthat/test-effects.R index 674908f..8df1153 100644 --- a/tests/testthat/test-effects.R +++ b/tests/testthat/test-effects.R @@ -113,7 +113,7 @@ test_that("a GAM reports effective degrees of freedom per smooth", { test_that("the engine object comes out for anything that plots it directly", { skip_on_cran() - # fancygam::plotSmooths() and friends take the mgcv fit, not the workflow. + # fancyfx::plotEffects() and friends take the mgcv fit, not the workflow. expect_s3_class(model_engine_fit(fitted_of_type("glm")), "glm") skip_if_not_installed("mgcv") expect_s3_class(model_engine_fit(fitted_of_type("gam")), "gam") @@ -173,10 +173,10 @@ test_that("the smoothed variables are read back off the fitted model", { expect_false(any(grepl("^s\\(", smoothed))) }) -test_that("fancygam draws the fitted smooths for a GAM", { +test_that("fancyfx draws the fitted smooths for a GAM", { skip_on_cran() skip_if_not_installed("mgcv") - skip_if_not_installed("fancygam") + skip_if_not_installed("fancyfx") model <- fitted_of_type("gam") plot <- suppressMessages(plot_gam_smooths(model)) @@ -194,7 +194,7 @@ test_that("fancygam draws the fitted smooths for a GAM", { test_that("smooth plots are drawn against the data the model actually saw", { skip_on_cran() skip_if_not_installed("mgcv") - skip_if_not_installed("fancygam") + skip_if_not_installed("fancyfx") model <- fitted_of_type("gam") # gratia reports the smooths in the recipe's output units, so the rug has to @@ -207,12 +207,12 @@ test_that("smooth plots are drawn against the data the model actually saw", { expect_gt(abs(mean(model$model_data$SST)), 1) }) -test_that("without fancygam the run still gets its generic curves", { +test_that("without fancyfx the run still gets its generic curves", { skip_on_cran() skip_if_not_installed("mgcv") - # fancygam is a Suggests: its absence removes the extra plot, not the + # fancyfx is a Suggests: its absence removes the extra plot, not the # diagnostics. - local_mocked_bindings(has_fancygam = function() FALSE) + local_mocked_bindings(has_fancyfx = function() FALSE) out <- tempfile("diag"); dir.create(out) suppressMessages(suppressWarnings(write_effect_plots(fitted_of_type("gam"), out))) written <- list.files(out)