Version 3.0 of visreg is a breaking release. This article explains what changed, why, and how to update your code. If you’d rather not update your code right now, see Keeping the old behavior below for how to keep using the pre-3.0 version.
What changed, and why
Over the years, visreg accumulated three separate plotting backends over the years: base R graphics, lattice, and ggplot2. Keeping all three in sync as features were added became increasingly difficult, especially since many options were much easier to implement using ggplot2. Furthermore, plot.visreg() was unpredictable in the sense that it could return completely different types of objects depending on how it was called.
In 3.0, the base R and lattice plotting code has been removed entirely: plot.visreg() now always builds and returns a ggplot2 object, which you can further modify with +, just as you would any other ggplot2 plot. The gg argument is gone because there is no longer a choice involved.
At the same time, the package’s internal and user-facing naming was inconsistent — a mix of camelCase, dotted.names, and snake_case left over from different eras of the codebase. 3.0 standardizes everything to snake_case. Most of this was internal and invisible to users, but a handful of user-facing argument names changed as a result (see the table below). A few arguments that only ever made sense for the old lattice/base-R backends (panel spacing, legend toggling) have been removed, since ggplot2 handles these automatically. Finally, type = "effect", a deprecated alias for type = "contrast" that had been printing a warning for years, has also been removed for good.
Renamed and removed arguments
gg |
(removed) |
plot.visreg() |
Plots are always ggplot2 now; there’s nothing to toggle. |
line.par |
line |
plot.visreg() |
|
fill.par |
fill |
plot.visreg() |
|
points.par |
points |
plot.visreg() |
|
print.cond |
print_cond |
plot.visreg() |
|
strip.names |
strip_names |
plot.visreg() |
|
legend |
(removed) |
plot.visreg() |
ggplot2 draws a legend automatically when one is needed; suppress it with + ggplot2::theme(legend.position = "none"). |
whitespace |
(removed) |
plot.visreg() |
Controlled panel spacing in the old lattice backend; use standard ggplot2/facet_grid() mechanisms if you need to adjust spacing. |
xtrans |
(removed) |
visreg() |
Change the x axis with ggplot2’s scale_x_*() functions |
type = "effect" |
type = "contrast" |
visreg(), visreg2d()
|
The deprecated alias has been fully removed. |
visregList() |
visreg_list() |
top-level function |
The function and the S3 class it returns ("visregList" → "visreg_list") were both renamed. |
plot.type = "image" |
(removed) |
visreg2d() |
Redundant with the raster plot below; use the default instead. |
plot.type = "gg" |
(removed — now the default) |
visreg2d() |
plot.visreg2d() always builds a ggplot2 object now; there’s nothing to select. |
plot.type = "persp" |
persp() |
visreg2d() |
Compute with plot = FALSE, then call persp() on the result: visreg2d(fit, "x", "y", plot = FALSE) \|> persp(). |
plot.type = "rgl" |
rgl::persp3d() |
visreg2d() |
Compute with plot = FALSE, then call rgl::persp3d() on the result. |
If you were passing any of the old dotted/camelCase names positionally rather than by name, that will still work — only the names themselves changed, not the argument order.
Renamed columns in the returned object
If your code reaches into the object returned by visreg() directly (rather than just plotting it), note that the columns of the fit and res data frames were also renamed to snake_case:
$fit$visregFit |
$fit$visreg_fit |
$fit$visregLwr |
$fit$visreg_lwr |
$fit$visregUpr |
$fit$visreg_upr |
$res$visregRes |
$res$visreg_res |
$res$visregPos |
$res$visreg_pos |
For example:
fit <- lm(Ozone ~ Solar.R + Wind + Temp, data = airquality)
v <- visreg(fit, "Wind", plot = FALSE)
head(v$fit[, c("Wind", "visreg_fit", "visreg_lwr", "visreg_upr")])
Wind visreg_fit visreg_lwr visreg_upr
1 2.300 70.88886 60.40958 81.36815
2 2.484 70.27548 60.01535 80.53561
3 2.668 69.66210 59.62023 79.70397
4 2.852 69.04872 59.22417 78.87327
5 3.036 68.43534 58.82708 78.04360
6 3.220 67.82196 58.42892 77.21500
A worked example
Old (2.8.1 and earlier) code that customized the appearance of a plot might have looked like this:
visreg(fit, "Wind", gg = TRUE,
line.par = list(col = "red"),
fill.par = list(fill = "green"),
points.par = list(cex = 2, pch = 1)
)
The 3.0 equivalent drops gg = TRUE (no longer needed) and renames the three styling arguments:
visreg(fit, "Wind",
line = list(color = "red"),
fill = list(fill = "green"),
points = list(size = 2, shape = 1)
)
Note that the values passed to line/fill/points are ggplot2 aesthetics (color, size, shape, …), not base R par() names (col, cex, pch) — this was already true whenever gg = TRUE was used pre-3.0, so it should look familiar if you were already using the ggplot2 backend. See Graphical options for the full set of arguments visreg() exposes, and Customizing with ggplot2 for everything else you can do to the plot it returns.
visreg2d() surface plots
plot.visreg2d() used to take a plot.type argument with four options ("image", "gg", "persp", "rgl") that behaved like four unrelated functions glued together — different return types, different side effects, and mostly-incompatible sets of graphical parameters. visreg 3.0 splits these apart by rendering paradigm:
-
plot(v) is now the only 2D flat plot, and it’s always a ggplot2 raster/contour plot (the old image type, a base R filled.contour, is gone — it was redundant with gg, which is now simply the default).
-
persp(v) — a static 3D perspective plot — is now a method for base R’s own persp() generic function.
-
rgl::persp3d(v) — the interactive, rotatable 3D plot — is likewise a method for rgl’s persp3d() generic.
visreg2d() no longer has a plot.type argument at all: plot = TRUE (the default) always draws the ggplot2 version. For a perspective or rgl plot, compute with plot = FALSE and pass the result along:
fit <- lm(
Ozone ~ Solar.R + Wind + Temp + I(Wind^2) + I(Temp^2),
data = airquality
)
visreg2d(fit, "Wind", "Temp", plot = FALSE) |> persp()
See the surface plots vignette for further examples, including rgl.
If you’re coming from base R graphics
If you were relying on the base R (or lattice) backend rather than gg = TRUE, the renamed arguments above aren’t the biggest adjustment — it’s that customization now happens by adding ggplot2 layers with +, rather than by passing more arguments to visreg() or calling base functions like title(), abline(), or par() afterward. The article Customizing with ggplot2 is aimed at helping users with this transition: how to add titles, transform axes, annotate with text, overlay smooths curve or other lines, and how to combine multiple plots into one image. These are all things that you would have used title(), abline(), text(), par(mfrow = ...), etc., to do; you can still do all these things, but you have to go through the ggplot2 infrastructure now.
Keeping the old behavior
If you’re not ready to update your code, you don’t have to: the last CRAN release before these changes, version 2.8.1, remains available and can be installed alongside or instead of the current version:
remotes::install_version("visreg", "2.8.1")