+
Both report helpful messages to the console about what decisions it
made, and how you can control those decisions.
@@ -90,7 +98,7 @@ autoplot(lm(Petal.Length ~ Sepal.Length + I(Sepal.Length^2):Species, iris))
#> F-statistic: 1655 on 4 and 145 DF, p-value: < 2.2e-16
```
-
+
`autoplot` makes a few decisions for you, but lets you control them. The
model’s first numeric predictor goes on the x-axis, and subsequent
@@ -109,7 +117,7 @@ model <- lm(hwy ~ displ + drv + cyl, data = mpg)
autoplot(model, aes(color = factor(cyl))) + facet_wrap(~drv)
```
-
+
``` r
@@ -120,7 +128,7 @@ autoplot(model,
labs(title = "The Same Model From a Different Angle")
```
-
+
### 3d
@@ -203,7 +211,7 @@ ggplot(dat, aes(x = log(5 + x + x2 + x3 + 1/5 * x * x3 + 80 * x4), y)) +
#> To turn this off, use 'back_transform = FALSE'.
```
-
+
Those assumptions had to be made, but you can make better ones. This
plot shows the same model far clearer by utilizing several features
@@ -218,7 +226,7 @@ ggplot(dat, aes(x, y, color = factor(x3))) +
geom_slice_subtitle()
```
-
+
### Choosing slices with `predict_vars`
@@ -238,7 +246,7 @@ ggplot(iris, aes(Petal.Length, Sepal.Length)) +
geom_slice_text()
```
-
+
### Confidence and prediction intervals
@@ -259,7 +267,7 @@ ggplot(mpg, aes(displ, hwy)) +
labs(title = "Prediction Interval")
```
-
+
### Projection bands
@@ -281,7 +289,7 @@ ggplot(mpg, aes(displ, hwy, color = drv)) +
geom_slice_text()
```
-
+
``` r
@@ -292,7 +300,7 @@ ggplot(mpg, aes(displ, hwy, color = drv)) +
geom_slice_text()
```
-
+
`band = TRUE` infers the variable when there is only one sensible choice
(the one multi-value `predict_vars` entry, or the single predictor the
@@ -327,7 +335,7 @@ ggplot(mtcars, aes(disp, mpg)) +
labs(title = "scale_y_log10() axis")
```
-

+

### Grouping: one line per group
@@ -349,7 +357,7 @@ ggplot(mpg, aes(displ, hwy, color = drv)) +
geom_slice(model, full_range = TRUE)
```
-
+
## Labeling the slices
@@ -371,7 +379,7 @@ ggplot(iris, aes(Petal.Length, Sepal.Length)) +
geom_slice_text()
```
-
+
``` r
set.seed(123)
@@ -391,7 +399,7 @@ ggplot(dat, aes(x, y, color = g)) +
geom_slice_text()
```
-
+
``` r
# Multiple predictors represent one line
@@ -401,7 +409,7 @@ ggplot(dat, aes(x, y)) +
geom_slice_text()
```
-
+
``` r
# Identical, with `style = "legend"`
@@ -411,7 +419,7 @@ ggplot(dat, aes(x, y)) +
geom_slice_text(style = "legend")
```
-
+
Options: `style` (`"variable"` writes `"hp: 66"`, `"value"` writes bare
values, `"legend"` adds a corner key), `location = "left"`/`"right"`,
@@ -432,7 +440,7 @@ ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +
geom_slice_subtitle() # or geom_slice_caption()
```
-
+
`geom_slice_subtitle()` will go to great lengths to keep the model
readable. It adds indents where there is room, and compresses where
@@ -460,7 +468,7 @@ slice_2d(model)
slice_2d(model, x_axis = "hp", disp = 250, n = 150, col = "blue", lwd = 2)
```
-
+
### `add_slice_2d()`
@@ -478,7 +486,7 @@ add_slice_2d(model, hp = min(mtcars$hp), col = "blue")
add_slice_2d(model, hp = max(mtcars$hp), col = "red", lty = 2)
```
-
+
------------------------------------------------------------------------
@@ -577,12 +585,12 @@ Enjoy clearer model visualizations with **Applr**!
Thanks to **Cameron McClellan** for his vision and initiation, **James
Beeson** for seeing it through, **Anthropic’s Claude** for solving the
-problems we couldn’t, and **Brother Saunders** - our teacher and
-inspiration to do great things.
+coding problems we couldn’t, and **Brother Garrett Saunders** - our
+teacher and inspiration to do great things.
And of course, Brigham Young University Idaho - our alma mater; the
school we love and cherish. And the God of Heaven and Earth, who gives
us all.
-33 For God is not the author of *confusion*, but of *peace*, as in all
-churches of the saints. - 1 Corinthians 14:33
+> “For God is not the author of *confusion*, but of *peace*, as in all
+> churches of the saints.” - 1 Corinthians 14:33
diff --git a/_pkgdown.yml b/_pkgdown.yml
index 7d2db16..2a27e13 100644
--- a/_pkgdown.yml
+++ b/_pkgdown.yml
@@ -26,9 +26,6 @@ reference:
- lm_equation
- lm_latex
- diagnose
-- title: Theme
- contents:
- - theme_lc
- title: ggproto objects
desc: Extension points underlying geom_slice.
contents:
diff --git a/cran-comments.md b/cran-comments.md
new file mode 100644
index 0000000..f883506
--- /dev/null
+++ b/cran-comments.md
@@ -0,0 +1,24 @@
+## R CMD check results
+
+0 errors | 0 warnings | 1 note
+
+* This is a new submission.
+
+## Test environments
+
+* local: Windows 11, R 4.5.x
+* win-builder: R-devel, R-release
+* mac-builder: R-release
+* GitHub Actions:
+ * R-release: macOS-latest, windows-latest, ubuntu-latest
+ * R-devel: ubuntu-latest
+
+## Notes for reviewers
+
+* `autoplot.lm()` prints `summary()` of the model by default, since the
+ function is designed for interactive model exploration. Users can
+ suppress it with `summary = FALSE`.
+* `lm_latex()` exists to print a LaTeX equation (e.g. in `results = "asis"`
+ chunks), so it writes with `cat()`; the string is also returned invisibly.
+* Examples that create 'plotly' widgets are wrapped in `if (interactive())`.
+* Thanks for your work taking time to review this.
diff --git a/debug.log b/debug.log
deleted file mode 100644
index 7ab2f84..0000000
--- a/debug.log
+++ /dev/null
@@ -1,4 +0,0 @@
-[0810/160819.407:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: The system cannot find the file specified. (0x2)
-[0810/160829.822:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: The system cannot find the file specified. (0x2)
-[0810/160838.555:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: The system cannot find the file specified. (0x2)
-[0810/162457.862:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: The system cannot find the file specified. (0x2)
diff --git a/for_devs/dev_todo.Rmd b/for_devs/dev_todo.Rmd
index f821cae..f7f5430 100644
--- a/for_devs/dev_todo.Rmd
+++ b/for_devs/dev_todo.Rmd
@@ -97,7 +97,7 @@ date: "2026-07-11"
- [x] Add error to check if the data being plotted is the same as the data the lm was fit to
- [x] `geom_slice`
- [x] Remove %>% in favor of |>
-- [x] `autoplot(lm)` (takes minimal arguments, feasts/ggtime is a good example) — DONE 2026-07-07 (`autoplot.lm(object, ..., x_axis =)`: scatter + geom_slice + theme_lc from the model's own data, first numeric predictor on x, `...` forwarded to geom_slice, `autoplot` generic re-exported; cases ap_01–ap_11)
+- [x] `autoplot(lm)` (takes minimal arguments, feasts/ggtime is a good example) — DONE 2026-07-07 (`autoplot.lm(object, ..., x_axis =)`: scatter + geom_slice from the model's own data, first numeric predictor on x, `...` forwarded to geom_slice, `autoplot` generic re-exported; cases ap_01–ap_11)
- [x] Add confidence and prediction interval to `geom_slice` — DONE 2026-07-07 (`interval = "confidence"/"prediction"`, GeomSlice now inherits GeomSmooth; cases gs_33/gs_34)
- [x] Add standards to `decisions.Rmd` (like error format)
- [x] Make sure existing ground truth tests are accurate
diff --git a/for_devs/release_plan_1.0.md b/for_devs/release_plan_1.0.md
index 70ea7e9..f8b861c 100644
--- a/for_devs/release_plan_1.0.md
+++ b/for_devs/release_plan_1.0.md
@@ -41,7 +41,7 @@ does and when to reach for it, and `@examples` that show the **range** of the
function (not one minimal call). Examples must run unattended in seconds;
wrap plotly/interactive ones in `if (interactive())`.
-- [x] **Prompt A** — utility docs pass: `theme_lc`, `lm_equation`/`lm_latex`,
+- [x] **Prompt A** — utility docs pass: `lm_equation`/`lm_latex`,
`scatter_3d`, `diagnose`, `get_inverse_function` (internal → `@noRd`).
Review note: two "Use it when…" paragraphs in `lm_equation.R` sat after
`@param clearer` (would merge into that param's doc) — fixed on review.
diff --git a/for_devs/scattered_notes.Rmd b/for_devs/scattered_notes.Rmd
index 1875cf6..0b58f54 100644
--- a/for_devs/scattered_notes.Rmd
+++ b/for_devs/scattered_notes.Rmd
@@ -287,6 +287,3 @@ Same
- Quadratic
- Exponential
-## theme_lc
-
-Cool. Captions?
diff --git a/man/Applr-package.Rd b/man/Applr-package.Rd
index 37301ee..f5ed650 100644
--- a/man/Applr-package.Rd
+++ b/man/Applr-package.Rd
@@ -29,33 +29,33 @@ Core capabilities:
\item ggplot2 labeling: \code{\link{geom_slice_text}}, \code{\link{geom_slice_subtitle}}, \code{\link{geom_slice_caption}}
\item 3D visualization: \code{\link{scatter_3d}}
\item Equations: \code{\link{lm_equation}}, \code{\link{lm_latex}}
-\item Theme: \code{\link{theme_lc}}
}
}
\section{Quick start}{
-Fit a model and plot a slice:
+Fit a model, then see it in one call:
\preformatted{
library(Applr)
-model <- lm(mpg ~ disp + hp, data = mtcars)
-slice_2d(model, x_axis = "disp")
+model <- lm(mpg ~ qsec + am, data = mtcars)
+autoplot(model)
}
-ggplot2 layer with facets:
+Or build the plot yourself with a geom_slice() layer:
\preformatted{
library(ggplot2)
-model <- lm(mpg ~ disp + hp + cyl, data = mtcars)
-ggplot(mtcars, aes(disp, mpg)) +
+model <- lm(hwy ~ displ * drv + cyl, data = mpg)
+ggplot(mpg, aes(displ, hwy, color = drv)) +
geom_point() +
- facet_wrap(~cyl) +
- geom_slice(model = model, predict_vars = list(hp = 110))
+ geom_slice(model, band = TRUE) +
+ geom_slice_text() +
+ geom_slice_subtitle()
}
}
\seealso{
\itemize{
-\item Function help pages: \code{?slice_2d}, \code{?geom_slice}, \code{?scatter_3d}, \code{?lm_equation}, \code{?lm_latex}, \code{?theme_lc}
+\item Function help pages: \code{?slice_2d}, \code{?geom_slice}, \code{?scatter_3d}, \code{?lm_equation}, \code{?lm_latex}
\item The package README for a brief tour
}
}
@@ -67,4 +67,9 @@ Authors:
\item Cameron McClellan \email{cameron.m.mcclellan@gmail.com}
}
+Other contributors:
+\itemize{
+ \item Garrett Saunders \email{saundersg@byui.edu} [thesis advisor]
+}
+
}
diff --git a/man/add_slice_2d.Rd b/man/add_slice_2d.Rd
index d5dbd5d..83fbbfd 100644
--- a/man/add_slice_2d.Rd
+++ b/man/add_slice_2d.Rd
@@ -51,10 +51,13 @@ than creating a new one. Useful for overlaying multiple model slices or adding
model predictions to scatter plots.
}
\examples{
-\dontrun{
-model <- lm(mpg ~ disp + hp, data = mtcars)
+model <- lm(log(mpg) ~ disp + hp, data = mtcars)
plot(mpg ~ disp, data = mtcars)
-add_slice_2d(model,x_axis='disp', hp=100)
-}
+add_slice_2d(model)
+
+# Multiple slices on one plot
+plot(mpg ~ disp, data = mtcars)
+add_slice_2d(model, hp = min(mtcars$hp), col = "blue")
+add_slice_2d(model, hp = max(mtcars$hp), col = "red", lty = 2)
}
diff --git a/man/autoplot.lm.Rd b/man/autoplot.lm.Rd
index 7ecc66e..73461b7 100644
--- a/man/autoplot.lm.Rd
+++ b/man/autoplot.lm.Rd
@@ -73,30 +73,28 @@ automatically back-transformed to match the raw y-axis.
\examples{
library(ggplot2)
-# A complete plot from a model in one call
-autoplot(lm(mpg ~ disp, data = mtcars))
+# A complete plot from a model in one call; summary() prints to the console
+autoplot(lm(Petal.Length ~ Sepal.Length + I(Sepal.Length^2):Species, iris))
-# Predictors not on the plot are imputed (a message says how)
-autoplot(lm(mpg ~ disp + hp, data = mtcars))
+# Predictors that group into a few values become color, then facets
+model <- lm(hwy ~ displ + drv + cyl, data = mpg)
+autoplot(model, summary = FALSE)
-# Choose which predictor goes on the x-axis
-autoplot(lm(mpg ~ disp + hp, data = mtcars), mapping = aes(x = hp))
+# Change the defaults like a normal ggplot; the rest is still inferred
+autoplot(model, aes(color = factor(cyl)), summary = FALSE) + facet_wrap(~drv)
-# Transformed response, back-transformed onto the raw mpg axis
-autoplot(lm(log(mpg) ~ disp, data = mtcars))
-
-# Options pass through to geom_slice(); the result is a normal ggplot
-autoplot(lm(mpg ~ disp + hp, data = mtcars),
- predict_vars = list(hp = 110), interval = "prediction") +
- labs(title = "Slice at hp = 110")
+# Choose the x-axis and pass geom_slice() options through
+autoplot(model,
+ aes(x = cyl, color = factor(drv)),
+ interval = "none", summary = FALSE) +
+ labs(title = "The Same Model From a Different Angle")
-# Extra aesthetics for the scatter (mapping goes to ggplot())
-autoplot(lm(mpg ~ hp + factor(cyl), data = mtcars),
- mapping = aes(color = factor(cyl)))
+# Transformed response, back-transformed onto the raw mpg axis
+autoplot(lm(log(mpg) ~ disp + hp, data = mtcars), summary = FALSE)
-# An interactive 3-D surface for a two-predictor model
+# An interactive 3-D surface for a model with two numeric predictors
if (interactive()) {
- autoplot(lm(mpg ~ disp + hp, data = mtcars), type = "3d")
+ autoplot(lm(mpg ~ wt + I(wt^2) + hp, data = mtcars), type = "3d")
}
}
diff --git a/man/figures/README-autoplot-mapping-1.png b/man/figures/README-autoplot-mapping-1.png
index f8652cf..12ec077 100644
Binary files a/man/figures/README-autoplot-mapping-1.png and b/man/figures/README-autoplot-mapping-1.png differ
diff --git a/man/figures/logo.svg b/man/figures/logo.svg
new file mode 100644
index 0000000..6170ccd
--- /dev/null
+++ b/man/figures/logo.svg
@@ -0,0 +1,56 @@
+
+
\ No newline at end of file
diff --git a/man/geom_fit.Rd b/man/geom_fit.Rd
index 37b6404..944ca68 100644
--- a/man/geom_fit.Rd
+++ b/man/geom_fit.Rd
@@ -9,6 +9,9 @@ geom_fit(...)
\arguments{
\item{...}{Ignored; accepted only so old calls reach the error message.}
}
+\value{
+None; always throws an error pointing to \code{\link[=geom_slice]{geom_slice()}}.
+}
\description{
\code{geom_fit()} was removed in favor of \code{\link[=geom_slice]{geom_slice()}}, which does everything
it did (prediction lines from a fitted \code{lm()}, confidence/prediction
diff --git a/man/geom_slice.Rd b/man/geom_slice.Rd
index 6c9fc6a..b04c245 100644
--- a/man/geom_slice.Rd
+++ b/man/geom_slice.Rd
@@ -91,69 +91,72 @@ and transformed scales (\code{scale_x_log10()}) are also handled.
\examples{
library(ggplot2)
-# Basic use, like geom_smooth() but drawing *your* model. hp is not on
-# the plot, so it is held at its mean (a console message says so).
-model <- lm(mpg ~ disp + hp, data = mtcars)
-ggplot(mtcars, aes(disp, mpg)) +
+# Basic use, like geom_smooth() but drawing *your* model. Sepal.Width is
+# not on the plot, so it is held at its mean (a console message says so).
+model <- lm(Sepal.Length ~ Sepal.Width + Petal.Length, data = iris)
+ggplot(iris, aes(Petal.Length, Sepal.Length)) +
geom_point() +
geom_slice(model)
-# Choose the slice yourself
-ggplot(mtcars, aes(disp, mpg)) +
- geom_point() +
- geom_slice(model, predict_vars = list(hp = 110))
-
# Several values draw one line per value (label them with
# geom_slice_text()); several multi-value variables are crossed
-ggplot(mtcars, aes(disp, mpg)) +
+ggplot(iris, aes(Petal.Length, Sepal.Length)) +
geom_point() +
- geom_slice(model, predict_vars = list(hp = c(66, 150, 335)))
+ geom_slice(model, predict_vars = list(Sepal.Width = c(2, 3, 4))) +
+ geom_slice_text()
# Confidence or prediction ribbon around the line
-ggplot(mtcars, aes(disp, mpg)) +
+model2 <- lm(hwy ~ displ + I(displ^2), data = mpg)
+ggplot(mpg, aes(displ, hwy)) +
geom_point() +
- geom_slice(model, predict_vars = list(hp = 110), interval = "confidence")
+ geom_slice(model2, interval = "prediction")
# A grouping aesthetic pins its predictor to each group's own value:
-# one line per cylinder count, each with its own slope
-model2 <- lm(mpg ~ disp * cyl, data = mtcars)
-ggplot(mtcars, aes(disp, mpg, color = factor(cyl))) +
+# one line per drive type, each stopping at the end of its group's data
+model3 <- lm(hwy ~ displ + I(displ^2) + displ:drv, data = mpg)
+ggplot(mpg, aes(displ, hwy, color = drv)) +
geom_point() +
- geom_slice(model2)
+ geom_slice(model3)
-# full_range = TRUE extends each group's line across the whole panel,
-# not just its own data range
-ggplot(mtcars, aes(disp, mpg, color = factor(cyl))) +
+# full_range = TRUE extends each group's line across the whole panel
+ggplot(mpg, aes(displ, hwy, color = drv)) +
geom_point() +
- geom_slice(model2, full_range = TRUE)
+ geom_slice(model3, full_range = TRUE)
# Facet variables pin per panel the same way
-ggplot(mtcars, aes(disp, mpg)) +
+ggplot(mpg, aes(displ, hwy)) +
geom_point() +
- geom_slice(model2) +
- facet_wrap(~ cyl)
+ geom_slice(model3) +
+ facet_wrap(~drv)
-# A transformed response is back-transformed automatically: the model
-# predicts log(mpg), the line appears in raw mpg units (a message says so)
-model3 <- lm(log(mpg) ~ disp + hp, data = mtcars)
-ggplot(mtcars, aes(disp, mpg)) +
+# A projection band shows the reach of a predictor, here cyl from 5 to 7;
+# band = TRUE infers the variable and its data range when it can
+model4 <- lm(hwy ~ displ * drv + cyl, data = mpg)
+ggplot(mpg, aes(displ, hwy, color = drv)) +
geom_point() +
- geom_slice(model3)
+ geom_slice(model4, band = "cyl", predict_vars = list(cyl = c(5, 7)))
-# ... or control the mapping yourself with back_transform
+# A transformed response is back-transformed to match the y-axis: the
+# model predicts log(mpg), the line appears in raw mpg units
+model5 <- lm(log(mpg) ~ disp + hp, data = mtcars)
ggplot(mtcars, aes(disp, mpg)) +
geom_point() +
- geom_slice(model3, back_transform = exp)
+ geom_slice(model5)
-# A projection band spanning hp between two values, instead of a line
+# ... adapts when the axis itself is transformed
+ggplot(mtcars, aes(disp, log(mpg))) +
+ geom_point() +
+ geom_slice(model5)
+
+# ... or control the mapping yourself with back_transform
ggplot(mtcars, aes(disp, mpg)) +
geom_point() +
- geom_slice(model, predict_vars = list(hp = c(66, 335)), band = "hp")
+ geom_slice(model5, back_transform = exp)
# n controls how many prediction points make up the line
ggplot(mtcars, aes(disp, mpg)) +
geom_point() +
- geom_slice(model, predict_vars = list(hp = 110), n = 10)
+ geom_slice(model5, n = 10)
}
\seealso{
diff --git a/man/geom_slice_caption.Rd b/man/geom_slice_caption.Rd
index c23c2a0..f7d57dd 100644
--- a/man/geom_slice_caption.Rd
+++ b/man/geom_slice_caption.Rd
@@ -53,11 +53,13 @@ or \code{\link[=geom_slice_text]{geom_slice_text()}} already label it, and every
\examples{
library(ggplot2)
-# Caption reports the model equation and that hp is held at its mean
-model <- lm(mpg ~ disp + hp, data = mtcars)
-ggplot(mtcars, aes(disp, mpg)) +
+# Caption reports the model equation and the held Sepal.Width; Species
+# is already shown by the legend, so it is left out
+model <- lm(Petal.Length ~ Sepal.Length + I(Sepal.Length^2):Species +
+ Sepal.Width, data = iris)
+ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +
geom_point() +
- geom_slice(model) +
+ geom_slice(model, interval = "confidence") +
geom_slice_caption()
}
diff --git a/man/geom_slice_subtitle.Rd b/man/geom_slice_subtitle.Rd
index 1b54ac7..bed6acf 100644
--- a/man/geom_slice_subtitle.Rd
+++ b/man/geom_slice_subtitle.Rd
@@ -62,11 +62,13 @@ the line is dropped entirely.
\examples{
library(ggplot2)
-# Subtitle reports the model equation and that hp is held at its mean
-model <- lm(mpg ~ disp + hp, data = mtcars)
-ggplot(mtcars, aes(disp, mpg)) +
+# Subtitle reports the model equation and the held Sepal.Width; Species
+# is already shown by the legend, so it is left out
+model <- lm(Petal.Length ~ Sepal.Length + I(Sepal.Length^2):Species +
+ Sepal.Width, data = iris)
+ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +
geom_point() +
- geom_slice(model) +
+ geom_slice(model, interval = "confidence") +
geom_slice_subtitle()
}
diff --git a/man/geom_slice_text.Rd b/man/geom_slice_text.Rd
index 241ea05..46874cd 100644
--- a/man/geom_slice_text.Rd
+++ b/man/geom_slice_text.Rd
@@ -63,19 +63,32 @@ inherits its line's color.
\examples{
library(ggplot2)
-# Two visually identical lines, told apart by their labels
-model <- lm(mpg ~ disp + hp, data = mtcars)
-ggplot(mtcars, aes(disp, mpg)) +
+# One label per predict_vars value
+model <- lm(Sepal.Length ~ Sepal.Width + Petal.Length, data = iris)
+ggplot(iris, aes(Petal.Length, Sepal.Length)) +
geom_point() +
- geom_slice(model, predict_vars = list(hp = c(66, 335))) +
+ geom_slice(model, predict_vars = list(Sepal.Width = c(2, 3, 4))) +
geom_slice_text()
# A projection band, labeled on each edge
-ggplot(mtcars, aes(disp, mpg)) +
+ggplot(iris, aes(Petal.Length, Sepal.Length)) +
geom_point() +
geom_slice(model, band = TRUE) +
geom_slice_text()
+# Labels take the color of their group
+model2 <- lm(hwy ~ displ * drv + cyl, data = mpg)
+ggplot(mpg, aes(displ, hwy, color = drv)) +
+ geom_point() +
+ geom_slice(model2, predict_vars = list(cyl = c(4, 8))) +
+ geom_slice_text()
+
+# Lines defined by several variables read best with a corner key
+ggplot(mpg, aes(displ, hwy)) +
+ geom_point() +
+ geom_slice(model2, predict_vars = list(cyl = c(4, 8), drv = c("f", "r"))) +
+ geom_slice_text(style = "legend")
+
}
\seealso{
\code{\link[=geom_slice]{geom_slice()}} for the layers being labeled;
diff --git a/man/scatter_3d.Rd b/man/scatter_3d.Rd
index 76aa9c7..d694ec0 100644
--- a/man/scatter_3d.Rd
+++ b/man/scatter_3d.Rd
@@ -34,16 +34,12 @@ fine — the surface is evaluated with \code{\link[=predict]{predict()}} on the
}
\examples{
if (interactive()) {
- # A flat regression plane over two predictors
- model <- lm(mpg ~ disp + hp, data = mtcars)
+ # Transformed terms graph over their raw predictors
+ model <- lm(mpg ~ wt + I(wt^2) + hp, data = mtcars)
scatter_3d(model)
# Custom marker gradient and a finer surface grid
- scatter_3d(model, n = 200, colors = c("blue", "yellow"))
-
- # Transformed terms graph over their raw predictors
- curved <- lm(mpg ~ wt + I(wt^2) + hp, data = mtcars)
- scatter_3d(curved)
+ scatter_3d(model, n = 200, colors = c("purple", "orange"))
# Interaction models produce a twisted (non-planar) surface
twisted <- lm(Sepal.Length ~ Sepal.Width * Petal.Length, data = iris)
diff --git a/man/slice_2d.Rd b/man/slice_2d.Rd
index 8b1d79d..6195bac 100644
--- a/man/slice_2d.Rd
+++ b/man/slice_2d.Rd
@@ -53,12 +53,13 @@ to be the first predictor in the model and all other variable are imputed
(mean for numeric, first level for factors).
}
\examples{
-\dontrun{
-model <- lm(mpg ~ disp, data = mtcars)
-slice_2d(model, x_axis = 'disp')
+model <- lm(log(mpg) ~ disp + hp, data = mtcars)
+slice_2d(model)
+
+# Choose the x-axis, hold disp at a value, and style the line
+slice_2d(model, x_axis = "hp", disp = 250, n = 150, col = "blue", lwd = 2)
# With a 95\% confidence band around the line
-slice_2d(model, x_axis = 'disp', interval = "confidence")
-}
+slice_2d(model, interval = "confidence")
}
diff --git a/man/theme_lc.Rd b/man/theme_lc.Rd
deleted file mode 100644
index ca04552..0000000
--- a/man/theme_lc.Rd
+++ /dev/null
@@ -1,50 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/theme_lc.R
-\name{theme_lc}
-\alias{theme_lc}
-\title{Theme based off of Lauren Condie's work}
-\usage{
-theme_lc()
-}
-\value{
-A complete ggplot2 theme object (class \code{"theme"}) that can be added
-to a plot with \code{+}.
-}
-\description{
-A custom ggplot2 theme that provides a cohesive, aesthetically pleasing color scheme
-and styling for statistical graphics. This theme sets both default geom aesthetics
-and plot theme elements to create a consistent visual style across all plots.
-The color palette uses warm, earthy tones that work well for academic and professional
-presentations. Reach for it when you want every plot in a document or
-presentation to share the same warm, muted look without styling each one
-by hand.
-}
-\details{
-Note that, in addition to returning a theme, calling \code{theme_lc()} sets
-session-wide geom defaults (point, line, smooth, segment, hline colors)
-via \code{\link[ggplot2:update_defaults]{ggplot2::update_geom_defaults()}}. These defaults persist for the rest
-of the R session, even on plots that don't use the theme.
-}
-\examples{
-library(ggplot2)
-
-# Scatterplot with the theme's warm-gray points and beige grid
-ggplot(mtcars, aes(disp, mpg)) +
- geom_point() +
- labs(title = "Fuel efficiency vs displacement") +
- theme_lc()
-
-# The theme also restyles lines and smooths
-ggplot(pressure, aes(temperature, pressure)) +
- geom_line() +
- geom_point() +
- theme_lc()
-
-# Works alongside faceting and further theme tweaks
-ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
- geom_point() +
- facet_wrap(~Species) +
- theme_lc() +
- theme(legend.position = "bottom")
-
-}
diff --git a/tests/_setup.R b/tests/_setup.R
index 1c25f9c..c6eea5a 100644
--- a/tests/_setup.R
+++ b/tests/_setup.R
@@ -12,6 +12,10 @@ if (!"Applr" %in% loadedNamespaces()) {
}
suppressPackageStartupMessages(library(ggplot2))
+# Straight quotes keep console snapshots identical across locales (a UTF-8
+# locale prints summary()'s significance codes with curly quotes).
+options(useFancyQuotes = FALSE)
+
# try_show(expr) — run one step of a console case. Prints the visible result,
# or the error/warning/message it raises, then lets the rest of the file keep
# running (a bare error would abort the script at the first failing call).
diff --git a/tests/cases/ap_01_single_predictor.R b/tests/cases/ap_01_single_predictor.R
index 80ef1f5..9010edd 100644
--- a/tests/cases/ap_01_single_predictor.R
+++ b/tests/cases/ap_01_single_predictor.R
@@ -4,7 +4,7 @@
# EXPECT: Complete plot built by autoplot(model) alone: mtcars scatter
# (disp on x, mpg on y), one straight downward-sloping slice line
# through the point cloud with its default confidence ribbon (a
-# narrow shaded band hugging the line), theme_lc styling.
+# narrow shaded band hugging the line).
# No errors, warnings, or imputation messages.
source("tests/_setup.R")
diff --git a/tests/cases/le_01_single_predictor.R b/tests/cases/le_01_single_predictor.R
index 0c26006..39cb2f1 100644
--- a/tests/cases/le_01_single_predictor.R
+++ b/tests/cases/le_01_single_predictor.R
@@ -2,7 +2,7 @@
# TYPE: console
# FUNC: lm_equation
# EXPECT: "y = Output (this run):
-Reference (ground truth):
Expect: “y =
+3 significant figures (the exact fit’s floating-point intercept is +zapped to 0).+ 1*x” with coefficients to -3 significant figures (intercept ~0 in scientific notation is fine).
Status: OK
# CASE: le_01_single_predictor
# TYPE: console
# FUNC: lm_equation
# EXPECT: "y = <intercept> + 1*x" with coefficients to 3 significant figures
-# (intercept ~0 in scientific notation is fine).
+# (the exact fit's floating-point intercept is zapped to 0).
source("tests/_setup.R")
set.seed(123)
@@ -7165,7 +7166,7 @@ le_01_single_predictor
try_show(lm_equation(model))
No plot produced by this run (expected for error cases).
Console output (this run):
-[1] "y = 6.28e-17 + 1*x"
+[1] "y = 0 + 1*x"
No plot produced by this run (expected for error cases).
Console output (this run):
-[1] "y = -3.39e-15 + 1*x + 1*x_pos"
+[1] "y = 0 + 1*x + 1*x_pos"
No plot produced by this run (expected for error cases).
Console output (this run):
-[1] "y = -1.51e-15 + 1*I(x * x_pos)"
+[1] "y = 0 + 1*I(x * x_pos)"
No plot produced by this run (expected for error cases).
Console output (this run):
-[1] "y = -2.51e-16 + 1*x:x_switch"
+[1] "y = 0 + 1*x:x_switch"
No plot produced by this run (expected for error cases).
Console output (this run):
-[1] "y = 8.29e-15 + 1*x + 1*I(x^2)"
+[1] "y = 0 + 1*x + 1*I(x^2)"
No plot produced by this run (expected for error cases).
Console output (this run):
-[1] "1/y = 6.28e-17 + 1*x"
+[1] "1/y = 0 + 1*x"
No plot produced by this run (expected for error cases).
Console output (this run):
-[1] "sqrt(y) = 2.01e-15 + 1*x_pos"
+[1] "sqrt(y) = 0 + 1*x_pos"
No plot produced by this run (expected for error cases).
Console output (this run):
-[1] "log(y) = 2.01e-15 + 1*x_pos"
+[1] "log(y) = 0 + 1*x_pos"
No plot produced by this run (expected for error cases).
Console output (this run):
-[1] "exp(y) = 2.01e-15 + 1*x_pos"
+[1] "exp(y) = 0 + 1*x_pos"
No plot produced by this run (expected for error cases).
Console output (this run):
-[1] "y^2 = 2.01e-15 + 1*x_pos"
+[1] "y^2 = 0 + 1*x_pos"
Expect: Complete plot built by autoplot(model) alone: mtcars scatter (disp on x, mpg on y), one straight downward-sloping slice line through the point cloud with its default -confidence ribbon (a narrow shaded band hugging the line), theme_lc -styling. No errors, warnings, or imputation messages.
+confidence ribbon (a narrow shaded band hugging the line). No errors, +warnings, or imputation messages.Status: OK
# CASE: ap_01_single_predictor
@@ -7681,7 +7682,7 @@ ap_01_single_predictor
# EXPECT: Complete plot built by autoplot(model) alone: mtcars scatter
# (disp on x, mpg on y), one straight downward-sloping slice line
# through the point cloud with its default confidence ribbon (a
-# narrow shaded band hugging the line), theme_lc styling.
+# narrow shaded band hugging the line).
# No errors, warnings, or imputation messages.
source("tests/_setup.R")
diff --git a/tests/testthat/test-deprecated.R b/tests/testthat/test-deprecated.R
new file mode 100644
index 0000000..4f8c43f
--- /dev/null
+++ b/tests/testthat/test-deprecated.R
@@ -0,0 +1,10 @@
+test_that("geom_fit() is removed and points to geom_slice()", {
+ expect_error(geom_fit(), "geom_slice")
+})
+
+test_that("drawit() warns and still draws", {
+ grDevices::pdf(NULL)
+ on.exit(grDevices::dev.off(), add = TRUE)
+ m <- lm(mpg ~ disp, data = mtcars)
+ expect_warning(drawit(m, xaxis = "disp"))
+})
diff --git a/tests/testthat/test-geom_slice.R b/tests/testthat/test-geom_slice.R
new file mode 100644
index 0000000..d51731a
--- /dev/null
+++ b/tests/testthat/test-geom_slice.R
@@ -0,0 +1,42 @@
+build_layer <- function(p, i = 1) ggplot2::ggplot_build(p)$data[[i]]
+
+m_simple <- lm(mpg ~ disp, data = mtcars)
+m_two <- lm(mpg ~ disp + hp, data = mtcars)
+base <- ggplot2::ggplot(mtcars, ggplot2::aes(disp, mpg))
+
+test_that("geom_slice() returns a ggplot2 layer", {
+ expect_s3_class(geom_slice(m_simple), "Layer")
+})
+
+test_that("the slice matches predict() across the data range", {
+ d <- build_layer(base + geom_slice(m_simple))
+ expect_equal(nrow(d), 100)
+ expect_equal(range(d$x), range(mtcars$disp))
+ expect_equal(d$y, unname(predict(m_simple, data.frame(disp = d$x))))
+})
+
+test_that("predict_vars holds other predictors at the given value", {
+ d <- build_layer(base + geom_slice(m_two, predict_vars = list(hp = 150)))
+ expect_equal(d$y, unname(predict(m_two, data.frame(disp = d$x, hp = 150))))
+})
+
+test_that("multiple predict_vars values draw one line each", {
+ d <- build_layer(base + geom_slice(m_two, predict_vars = list(hp = c(100, 200))))
+ expect_length(unique(d$group), 2)
+})
+
+test_that("interval = 'confidence' adds a band around the line", {
+ d <- build_layer(base + geom_slice(m_simple, interval = "confidence"))
+ expect_true(all(c("ymin", "ymax") %in% names(d)))
+ expect_true(all(d$ymin < d$y & d$y < d$ymax))
+})
+
+test_that("a log response is back-transformed to the plotted scale", {
+ m_log <- lm(log(mpg) ~ disp, data = mtcars)
+ expect_message(d <- build_layer(base + geom_slice(m_log)), "back-transformed")
+ expect_equal(d$y, unname(exp(predict(m_log, data.frame(disp = d$x)))))
+})
+
+test_that("geom_slice() rejects non-lm input", {
+ expect_error(ggplot2::ggplot_build(base + geom_slice("x")), "fitted by `lm\\(\\)`")
+})
diff --git a/tests/testthat/test-lm_equation.R b/tests/testthat/test-lm_equation.R
new file mode 100644
index 0000000..e582a5e
--- /dev/null
+++ b/tests/testthat/test-lm_equation.R
@@ -0,0 +1,27 @@
+m_simple <- lm(mpg ~ disp, data = mtcars)
+
+test_that("lm_equation() writes a plain-text equation", {
+ expect_identical(lm_equation(m_simple), "mpg = 29.6 - 0.0412*disp")
+ expect_identical(lm_equation(m_simple, style = "raw"), "mpg = 29.6 - 0.0412*disp")
+})
+
+test_that("lm_equation() keeps a transformed response on the left side", {
+ m_log <- lm(log(mpg) ~ disp, data = mtcars)
+ expect_identical(lm_equation(m_log), "log(mpg) = 3.45 - 0.00212*disp")
+})
+
+test_that("lm_equation() spells out factor levels", {
+ m_factor <- lm(mpg ~ disp + factor(cyl), data = mtcars)
+ eq <- lm_equation(m_factor)
+ expect_match(eq, "factor(cyl)=\"6\"", fixed = TRUE)
+ expect_match(eq, "factor(cyl)=\"8\"", fixed = TRUE)
+})
+
+test_that("lm_latex() returns display-math LaTeX", {
+ tex <- NULL
+ invisible(capture.output(tex <- lm_latex(m_simple)))
+ expect_type(tex, "character")
+ expect_match(tex, "^\\$\\$.*\\$\\$$")
+ expect_match(tex, "\\hat{Y_i}", fixed = TRUE)
+ expect_match(tex, "0.0412", fixed = TRUE)
+})
diff --git a/tests/testthat/test-plots.R b/tests/testthat/test-plots.R
new file mode 100644
index 0000000..dc86e5d
--- /dev/null
+++ b/tests/testthat/test-plots.R
@@ -0,0 +1,31 @@
+m_simple <- lm(mpg ~ disp, data = mtcars)
+m_two <- lm(mpg ~ disp + hp, data = mtcars)
+
+test_that("autoplot() builds a ggplot and summary = FALSE keeps it quiet", {
+ expect_silent(p <- ggplot2::autoplot(m_simple, summary = FALSE))
+ expect_s3_class(p, "ggplot")
+ expect_no_error(ggplot2::ggplot_build(p))
+})
+
+test_that("autoplot() prints the model summary by default", {
+ expect_output(ggplot2::autoplot(m_simple), "Residual standard error")
+})
+
+test_that("slice_2d() and add_slice_2d() draw in base graphics", {
+ grDevices::pdf(NULL)
+ on.exit(grDevices::dev.off(), add = TRUE)
+ expect_no_error(slice_2d(m_simple, x_axis = "disp"))
+ expect_no_error(slice_2d(m_simple, x_axis = "disp", interval = "confidence"))
+ plot(mpg ~ disp, data = mtcars)
+ expect_no_error(add_slice_2d(m_two, x_axis = "disp", hp = 100))
+})
+
+test_that("slice_2d() errors on an x_axis not in the model", {
+ grDevices::pdf(NULL)
+ on.exit(grDevices::dev.off(), add = TRUE)
+ expect_error(slice_2d(m_simple, x_axis = "nope"), "not found in the model")
+})
+
+test_that("scatter_3d() returns a plotly widget", {
+ expect_s3_class(scatter_3d(m_two), "plotly")
+})
diff --git a/vignettes/Applr.Rmd b/vignettes/Applr.Rmd
index 6c78dcb..f273fa8 100644
--- a/vignettes/Applr.Rmd
+++ b/vignettes/Applr.Rmd
@@ -17,120 +17,274 @@ knitr::opts_chunk$set(
```
A fitted linear model with more than one predictor lives in more dimensions
-than a scatterplot can show. The usual workaround — `geom_smooth(method =
-"lm")` — quietly fits a *different*, single-predictor model to the plotted
-variables, so the line on the screen is not the model you summarized.
+than a scatterplot can show. The usual workaround, `geom_smooth(method =
+"lm")`, quietly fits a *different*, simpler model to the plotted variables, so
+the line on the screen is not the model you summarized.
Applr draws the model you actually fit. Its core idea is the **slice**: hold
every predictor except one at a fixed value, and draw the model's predictions
-as that one predictor varies. This vignette walks through the workflow on a
-single dataset, `mtcars`.
+as that one predictor varies. Start by fitting an `lm()`, then hand it to
+Applr. Explore it with `autoplot()`, or present it with polish and
+fine-grained control using `geom_slice()`.
```{r setup, message = FALSE}
library(Applr)
library(ggplot2)
```
-## Fit a model, then autoplot() it
+## Explore with autoplot()
-Start with a model where `mpg` depends on two predictors:
+`autoplot()` on an `lm` builds the whole plot in one call: the model's own data
+as a scatter, a fitted slice line with its confidence ribbon, and a subtitle
+saying what the model is. It also prints `summary()` to the console, so you
+can refit, re-plot, and read the coefficients and R² in one step (turn this off
+with `summary = FALSE`).
```{r}
-model <- lm(mpg ~ disp + hp, data = mtcars)
-lm_equation(model)
+autoplot(lm(Petal.Length ~ Sepal.Length + I(Sepal.Length^2):Species, iris))
```
-`autoplot()` is the quickest way to see it. It rebuilds the scatterplot from
-the model's own data, picks the first numeric predictor for the x-axis, and
-adds a slice line with its confidence ribbon — plus a subtitle recording
-which values the other predictors were held at:
+`autoplot()` makes a few decisions for you. The model's first numeric
+predictor goes on the x-axis, and predictors that group into a few discrete
+values become `color`, then facets. Every decision can be overridden. Pass a
+`mapping` to change the defaults, just as you would with `ggplot()`; whatever
+you leave out is still inferred. Choose a different x-axis with
+`aes(x = ...)`. Arguments in `...` pass through to `geom_slice()`, so
+`interval = "prediction"` gives the wider ribbon and `interval = "none"` a
+bare line.
-```{r}
-autoplot(model)
-```
+```{r, results = "hide"}
+model <- lm(hwy ~ displ + drv + cyl, data = mpg)
-The message tells you what happened: `hp` was not on an axis, so the line
-holds it at its mean. Use `mapping = aes(x = ...)` to slice along a different
-predictor, and pass any `geom_slice()` argument through `...`:
+autoplot(model, aes(color = factor(cyl))) + facet_wrap(~drv)
-```{r}
-autoplot(model, mapping = aes(x = hp), interval = "prediction")
+autoplot(model,
+ aes(x = cyl, color = factor(drv)),
+ interval = "none") +
+ labs(title = "The Same Model From a Different Angle")
```
-## Build the plot yourself with geom_slice()
+The result is an ordinary ggplot, so `+` works as usual.
+
+## Present with geom_slice()
+
+`geom_slice()` is the layer underneath `autoplot()`. It looks like
+`geom_smooth()`, but where `geom_smooth()` fits its own model to the plotted
+data, `geom_slice()` draws the model *you* fitted, reading the plot's `aes()`
+to learn which predictor is on the x-axis. Unlike `autoplot()`, it makes as
+few decisions as it can.
+
+Once a model has more than one predictor, each line is a 2-D slice through it.
+Every predictor not on the x-axis has to be held at some value, and
+`geom_slice()` fills those values in this order:
+
+- Variables named in `predict_vars` are held at your chosen values.
+- Variables mapped to a grouping aesthetic (`aes(color = g)`) are held at each
+ group's value, giving one line per group.
+- Facet variables are held at each panel's value.
+- Anything left over is imputed (mean for numeric, most common value for
+ factors), with a console message naming the value used.
+
+The model below has four predictors and was fitted exactly, with no noise. A
+bare `geom_slice()` has to impute three of them, and the console messages
+report each choice:
+
+```{r, message = TRUE, fig.show = "hold", out.width = "49%", fig.width = 4, fig.height = 3.2}
+set.seed(123)
+n <- 80
+x <- runif(n, 0.1, 10)
+x2 <- runif(n, 0.1, 10)
+x3 <- sample(c(1, 20, 40), n, replace = TRUE)
+x4 <- sample(c(1, 2), n, replace = TRUE)
+y <- log(5 + x + x2 + x3 + 1/5 * x * x3 + 80 * x4) # no random noise
+dat <- data.frame(x, x2, x3, x4, y)
+
+model <- lm(exp(y) ~ x*x3 + x2 + x4, data = dat) # exact model fit
+
+ggplot(dat, aes(x, y)) +
+ geom_point() +
+ geom_slice(model) +
+ labs(title = "A Perfectly Fit Model 'Slice'")
+
+# The same model against its full linear predictor: an exact fit
+ggplot(dat, aes(x = log(5 + x + x2 + x3 + 1/5 * x * x3 + 80 * x4), y)) +
+ geom_point() +
+ geom_slice(model) +
+ labs(title = "Same Model, Different X-Axis")
+```
-For full control, add `geom_slice()` to an ordinary ggplot. The layer reads
-the plot's `aes()` to learn which predictor is on x, and predicts from your
-model — no separate model is fit:
+Notice that the response `exp(y)` was back-transformed onto the plotted `y`
+axis without being asked. Those imputations were forced, but you can do better.
+Map `x3` to color, facet by `x4`, and show the reach of `x2` as a band, and
+every line lands on its points:
```{r}
-ggplot(mtcars, aes(x = disp, y = mpg)) +
+ggplot(dat, aes(x, y, color = factor(x3))) +
geom_point() +
- geom_slice(model)
+ geom_slice(model, band = "x2", predict_vars = list(x2 = c(0, 10))) +
+ facet_wrap(~x4) +
+ geom_slice_text() +
+ geom_slice_subtitle()
```
-### Choosing the slice
+The rest of this vignette goes through those features one at a time.
-`predict_vars` pins the off-axis predictors to values you choose instead of
-their means. Give a predictor several values and you get one line per value —
-a small multiple of slices through the same model:
+### Choosing slices with predict_vars
+
+Give a variable several values in `predict_vars` to draw one line per value.
+Several multi-value variables are crossed.
```{r}
-ggplot(mtcars, aes(x = disp, y = mpg)) +
+model <- lm(Sepal.Length ~ Sepal.Width + Petal.Length, iris)
+
+ggplot(iris, aes(Petal.Length, Sepal.Length)) +
geom_point() +
- geom_slice(model, predict_vars = list(hp = c(66, 150, 335)))
+ geom_slice(
+ model,
+ predict_vars = list(Sepal.Width = c(2, 3, 4)),
+ interval = "confidence"
+ ) +
+ geom_slice_text()
```
-### Intervals
+### Confidence and prediction intervals
`interval = "confidence"` or `"prediction"` shades the corresponding
`predict.lm()` interval around each line:
+```{r, fig.show = "hold", out.width = "49%", fig.width = 4, fig.height = 3.2}
+model <- lm(hwy ~ displ + I(displ^2), data = mpg)
+
+ggplot(mpg, aes(displ, hwy)) +
+ geom_point() +
+ geom_slice(model, interval = "confidence") +
+ labs(title = "Confidence Interval")
+
+ggplot(mpg, aes(displ, hwy)) +
+ geom_point() +
+ geom_slice(model, interval = "prediction") +
+ labs(title = "Prediction Interval")
+```
+
+### Projection bands
+
+An interval shows uncertainty. A `band` shows the *reach of a predictor*: two
+edge slices with a translucent ribbon between them. `band = "variable"` spans
+that predictor between the values given in `predict_vars`, or across its
+observed range if `predict_vars` doesn't mention it. `band = TRUE` infers the
+variable when there is only one sensible choice.
+
```{r}
-ggplot(mtcars, aes(x = disp, y = mpg)) +
+model <- lm(hwy ~ displ * drv + cyl, data = mpg)
+
+# Inferred: cyl is the only predictor not shown, so it spans 4 to 8
+ggplot(mpg, aes(displ, hwy, color = drv)) +
+ geom_point() +
+ geom_slice(model, band = TRUE) +
+ geom_slice_text()
+
+# Specified: cyl from 5 to 7
+ggplot(mpg, aes(displ, hwy, color = drv)) +
geom_point() +
- geom_slice(model, predict_vars = list(hp = 150), interval = "prediction")
+ geom_slice(model, band = "cyl", predict_vars = list(cyl = c(5, 7))) +
+ geom_slice_text()
```
-## Labeling the slices
+`band` cannot be combined with `interval`.
-Three companion helpers describe the slices on the plot itself, so a reader
-can tell which line is which and what was held constant.
+### Transformed responses
-`geom_slice_text()` labels the end of each line with its held value;
-`geom_slice_caption()` writes the slice description under the plot:
+If the model's response is transformed but the plot shows something else,
+predictions are back-transformed to match the y-axis, and a console message
+says so. Turn this off with `back_transform = FALSE`, or pass a function or
+name (`exp`, `"log10"`, ...) to override the auto-detection.
-```{r}
-ggplot(mtcars, aes(x = disp, y = mpg)) +
+```{r, fig.show = "hold", out.width = "33%", fig.width = 2.5, fig.height = 3.2}
+model <- lm(log(mpg) ~ disp + hp, data = mtcars)
+
+ggplot(mtcars, aes(disp, mpg)) +
geom_point() +
- geom_slice(model, predict_vars = list(hp = c(66, 150, 335))) +
- geom_slice_text() +
- geom_slice_caption()
+ geom_slice(model) +
+ labs(title = "normal y axis")
+
+ggplot(mtcars, aes(disp, log(mpg))) +
+ geom_point() +
+ geom_slice(model) +
+ labs(title = "log() y axis")
+
+ggplot(mtcars, aes(disp, mpg)) +
+ geom_point() +
+ geom_slice(model) +
+ scale_y_log10() +
+ labs(title = "scale_y_log10() axis")
+```
+
+### Grouping: one line per group
+
+With a grouping aesthetic, each group gets its own slice, and each line stops
+at the end of its group's data. `full_range = TRUE` extends every line across
+the whole panel instead, like `fullrange` in `geom_smooth()`:
+
+```{r, fig.show = "hold", out.width = "49%", fig.width = 4, fig.height = 3.2}
+model <- lm(hwy ~ displ + I(displ^2) + displ:drv, data = mpg)
+
+ggplot(mpg, aes(displ, hwy, color = drv)) +
+ geom_point() +
+ geom_slice(model)
+
+ggplot(mpg, aes(displ, hwy, color = drv)) +
+ geom_point() +
+ geom_slice(model, full_range = TRUE)
```
-(`geom_slice_subtitle()` is the third helper — it puts the model equation and
-held values in the subtitle, and is what `autoplot()` uses by default.)
+## Labeling the slices
+
+Three companion layers describe the slices on the plot itself, so a reader can
+tell which line is which and what was held constant. They take no model; they
+read everything from the plot's `geom_slice()` layers, so add them *after*
+those layers.
-## Grouping and faceting
+`geom_slice_text()` labels the end of each line with the values that define
+it. It is most useful with bands and multi-value `predict_vars`:
+
+```{r, fig.width = 7.5}
+set.seed(123)
+n <- 80
+x <- runif(n, -10, 10)
+x2 <- runif(n, 0, 4)
+g <- factor(sample(c("A", "B"), n, replace = TRUE))
+y <- x * ifelse(g == "A", 0.5, 2) + ifelse(g == "A", 5, -5) + 4 * x2 + rnorm(n)
+dat <- data.frame(x, x2, g, y)
+model <- lm(y ~ x * g + x2, data = dat)
+
+ggplot(dat, aes(x, y)) +
+ geom_point(alpha = 0.5) +
+ geom_slice(model, predict_vars = list(x2 = c(0, 2, 4), g = c("A", "B"))) +
+ geom_slice_text(style = "legend")
+```
-Slices respect the plot's grouping and faceting. With a model that includes
-the grouping variable, each group gets its own correctly-sliced line:
+`geom_slice_subtitle()` (or `geom_slice_caption()`) writes the model equation
+and any held values that aren't already shown by the legend, facets, or
+`geom_slice_text()`. It indents the equation when there's room and compresses
+it when space is tight.
```{r}
-model_cyl <- lm(mpg ~ disp * cyl, data = mtcars)
+model <- lm(Petal.Length ~ Sepal.Length + I(Sepal.Length^2):Species + Sepal.Width, iris)
-ggplot(mtcars, aes(x = disp, y = mpg, color = factor(cyl))) +
+ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +
geom_point() +
- geom_slice(model_cyl)
+ geom_slice(model, interval = "confidence") +
+ geom_slice_subtitle()
```
## Where to go next
-- `?geom_slice` covers the remaining options: projection bands (`band`),
- extending lines across the panel (`full_range`), transformed responses and
- `back_transform`, and the `n` resolution of the line.
-- `?slice_2d` is the base-graphics equivalent of this whole workflow.
-- `?scatter_3d` (or `autoplot(model, type = "3d")`) shows a two-predictor
- model as an interactive 3-D surface instead of a slice.
+- `?geom_slice` documents every option, including `n`, the number of points
+ that make up each line.
+- `?geom_slice_text` and `?geom_slice_subtitle` cover label styles,
+ placement, and `prepend`/`append` text.
+- `?slice_2d` and `?add_slice_2d` draw the same slices in base graphics.
+- `?scatter_3d` (or `autoplot(model, type = "3d")`) shows a model with two
+ numeric predictors as an interactive 3-D surface.
- `?lm_equation` and `?lm_latex` turn the fitted model into text or LaTeX for
- reports.
+ reports, and `?diagnose` draws the standard residual plots.