Skip to content

Commit bcb87fc

Browse files
authored
Move {mgcv} to suggests (#5987)
* move all method setup to `setup_params()` * fallback to `method = "lm"` in absence of {mgcv} * adjust tests * move {mgcv} from Imports to Suggests * add news bullet * Revert "fallback to `method = "lm"` in absence of {mgcv}" This reverts commit 5824b1d. * homebrew an install prompt * change fallback * fix `gam_method()` in absence of mgcv * tweak message
1 parent 8ca3bbc commit bcb87fc

File tree

5 files changed

+86
-26
lines changed

5 files changed

+86
-26
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ Imports:
3939
isoband,
4040
lifecycle (> 1.0.1),
4141
MASS,
42-
mgcv,
4342
rlang (>= 1.1.0),
4443
scales (>= 1.3.0),
4544
stats,
@@ -55,6 +54,7 @@ Suggests:
5554
knitr,
5655
mapproj,
5756
maps,
57+
mgcv,
5858
multcomp,
5959
munsell,
6060
nlme,

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# ggplot2 (development version)
22

3+
* Moved {mgcv} from Imports to Suggests (@teunbrand, #5986)
34
* New `reset_geom_defaults()` and `reset_stat_defaults()` to restore all geom or
45
stat default aesthetics at once (@teunbrand, #5975).
56
* `facet_wrap()` can have `space = "free_x"` with 1-row layouts and

R/stat-smooth.R

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -95,36 +95,63 @@ StatSmooth <- ggproto("StatSmooth", Stat,
9595
setup_params = function(data, params) {
9696
params$flipped_aes <- has_flipped_aes(data, params, ambiguous = TRUE)
9797
msg <- character()
98-
if (is.null(params$method) || identical(params$method, "auto")) {
98+
method <- params$method
99+
if (is.null(method) || identical(method, "auto")) {
99100
# Use loess for small datasets, gam with a cubic regression basis for
100101
# larger. Based on size of the _largest_ group to avoid bad memory
101102
# behaviour of loess
102103
max_group <- max(table(interaction(data$group, data$PANEL, drop = TRUE)))
103104

104105
if (max_group < 1000) {
105-
params$method <- "loess"
106+
method <- "loess"
106107
} else {
107-
params$method <- "gam"
108+
method <- "gam"
108109
}
109-
msg <- c(msg, paste0("method = '", params$method, "'"))
110+
msg <- c(msg, paste0("method = '", method, "'"))
111+
}
112+
113+
if (identical(method, "gam") &&
114+
!prompt_install("mgcv", "for using {.code method = \"gam\"}")) {
115+
cli::cli_inform(c(
116+
"The {.arg method} was set to {.val gam}, but {.pkg mgcv} is not installed.",
117+
"!" = "Falling back to {.code method = \"lm\"}.",
118+
i = "Install {.pkg mgcv} or change the {.arg method} argument to \\
119+
resolve this issue."
120+
))
121+
method <- "lm"
110122
}
111123

112124
if (is.null(params$formula)) {
113-
if (identical(params$method, "gam")) {
125+
if (identical(method, "gam")) {
114126
params$formula <- y ~ s(x, bs = "cs")
115127
} else {
116128
params$formula <- y ~ x
117129
}
118130
msg <- c(msg, paste0("formula = '", deparse(params$formula), "'"))
119131
}
120-
if (identical(params$method, "gam")) {
121-
params$method <- gam_method()
132+
133+
# Special case span because it's the most commonly used model argument
134+
if (identical(method, "loess")) {
135+
params$method.args$span <- params$span %||% 0.75
136+
}
137+
138+
if (is.character(method)) {
139+
if (identical(method, "gam")) {
140+
method <- gam_method()
141+
} else {
142+
method <- match.fun(method)
143+
}
144+
}
145+
# If gam and gam's method is not specified by the user then use REML
146+
if (identical(method, gam_method())) {
147+
params$method.args$method <- params$method.args$method %||% "REML"
122148
}
123149

124150
if (length(msg) > 0) {
125151
cli::cli_inform("{.fn geom_smooth} using {msg}")
126152
}
127153

154+
params$method <- method
128155
params
129156
},
130157

@@ -159,23 +186,6 @@ StatSmooth <- ggproto("StatSmooth", Stat,
159186
}
160187
}
161188

162-
# Special case span because it's the most commonly used model argument
163-
if (identical(method, "loess")) {
164-
method.args$span <- span
165-
}
166-
167-
if (is.character(method)) {
168-
if (identical(method, "gam")) {
169-
method <- gam_method()
170-
} else {
171-
method <- match.fun(method)
172-
}
173-
}
174-
# If gam and gam's method is not specified by the user then use REML
175-
if (identical(method, gam_method()) && is.null(method.args$method)) {
176-
method.args$method <- "REML"
177-
}
178-
179189
prediction <- try_fetch(
180190
{
181191
model <- inject(method(
@@ -205,4 +215,10 @@ StatSmooth <- ggproto("StatSmooth", Stat,
205215
)
206216

207217
# This function exists to silence an undeclared import warning
208-
gam_method <- function() mgcv::gam
218+
gam_method <- function() {
219+
if (is_installed("mgcv")) {
220+
mgcv::gam
221+
} else {
222+
NA
223+
}
224+
}

R/utilities.R

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,3 +846,32 @@ as_unordered_factor <- function(x) {
846846
class(x) <- setdiff(class(x), "ordered")
847847
x
848848
}
849+
850+
# TODO: Replace me if rlang/#1730 gets implemented
851+
# Similar to `rlang::check_installed()` but returns boolean and misses
852+
# features such as versions, comparisons and using {pak}.
853+
prompt_install <- function(pkg, reason = NULL) {
854+
if (length(pkg) < 1 || is_installed(pkg)) {
855+
return(TRUE)
856+
}
857+
if (!interactive()) {
858+
return(FALSE)
859+
}
860+
861+
pkg <- pkg[!vapply(pkg, is_installed, logical(1))]
862+
863+
message <- "The {.pkg {pkg}} package{?s} {?is/are} required"
864+
if (is.null(reason)) {
865+
message <- paste0(message, ".")
866+
} else {
867+
message <- paste0(message, " ", reason)
868+
}
869+
question <- "Would you like to install {cli::qty(pkg)}{?it/them}?"
870+
871+
cli::cli_bullets(c("!" = message, "i" = question))
872+
if (utils::menu(c("Yes", "No")) != 1) {
873+
return(FALSE)
874+
}
875+
utils::install.packages(pkg)
876+
is_installed(pkg)
877+
}

tests/testthat/test-geom-smooth.R

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ test_that("default smoothing methods for small and large data sets work", {
5757
y = x^2 + 0.5 * rnorm(1001)
5858
)
5959

60+
skip_if_not_installed("mgcv")
61+
6062
m <- mgcv::gam(y ~ s(x, bs = "cs"), data = df, method = "REML")
6163
range <- range(df$x, na.rm = TRUE)
6264
xseq <- seq(range[1], range[2], length.out = 80)
@@ -96,6 +98,18 @@ test_that("geom_smooth() works when one group fails", {
9698
expect_gte(nrow(ld), 2)
9799
})
98100

101+
test_that("a fallback message is thrown when `method = 'gam'` and {mgcv} is absent", {
102+
p <- ggplot(mpg, aes(displ, hwy)) +
103+
geom_smooth(method = "gam", formula = y ~ x)
104+
105+
with_mocked_bindings(
106+
expect_message(
107+
ggplot_build(p), regexp = "Falling back to `method = \"lm\"`"
108+
),
109+
is_installed = function(...) FALSE
110+
)
111+
})
112+
99113
# Visual tests ------------------------------------------------------------
100114

101115
test_that("geom_smooth() works with alternative stats", {

0 commit comments

Comments
 (0)