Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@
^docs$
^pkgdown$
^\.github$
^debug\.log$
^cran-comments\.md$
^scratch$
4 changes: 2 additions & 2 deletions .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
R_KEEP_PKG_SOURCE: yes

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- uses: r-lib/actions/setup-pandoc@v2

Expand Down Expand Up @@ -61,7 +61,7 @@ jobs:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- uses: r-lib/actions/setup-r@v2
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pkgdown.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- uses: r-lib/actions/setup-pandoc@v2

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ Rplots.pdf
docs/
inst/doc/
scratch/
debug.log
12 changes: 8 additions & 4 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
Package: Applr
Title: Visualize and Explain Linear Models
Version: 0.0.0.9000
Version: 1.0.0
Authors@R: c(
person("Cameron", "McClellan", , "cameron.m.mcclellan@gmail.com", role = "aut"),
person("James", "Beeson", , "jamesbeeson01@gmail.com", role = c("aut", "cre")))
person("James", "Beeson", , "jamesbeeson01@gmail.com", role = c("aut", "cre")),
person("Garrett", "Saunders", , "saundersg@byui.edu", role = "ths"))
Description: Tools for visualizing and explaining linear models fit with lm().
Draws 2D slices of higher-dimensional models in base graphics and as a
ggplot2 layer, offers an interactive 3D scatter with a fitted surface for
two-predictor models, and renders model equations as plain text or LaTeX.
'ggplot2' layer, offers an interactive 'plotly' 3D scatter with a fitted
surface for two-predictor models, and renders model equations as plain
text or 'LaTeX'.
License: MIT + file LICENSE
URL: https://saundersg.github.io/Applr/, https://github.com/saundersg/Applr
BugReports: https://github.com/saundersg/Applr/issues
Encoding: UTF-8
Language: en-US
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.3
Depends:
R (>= 4.1.0)
Imports:
ggplot2 (>= 3.5.0),
plotly,
Expand Down
4 changes: 3 additions & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@ export(lm_equation)
export(lm_latex)
export(scatter_3d)
export(slice_2d)
export(theme_lc)
import(ggplot2)
importFrom(ggplot2,autoplot)
importFrom(grDevices,adjustcolor)
importFrom(graphics,abline)
importFrom(graphics,lines)
importFrom(graphics,mtext)
importFrom(graphics,par)
importFrom(graphics,plot)
importFrom(graphics,polygon)
importFrom(graphics,text)
importFrom(plotly,add_markers)
importFrom(plotly,add_trace)
importFrom(plotly,layout)
Expand All @@ -41,6 +42,7 @@ importFrom(stats,coef)
importFrom(stats,delete.response)
importFrom(stats,formula)
importFrom(stats,get_all_vars)
importFrom(stats,lowess)
importFrom(stats,model.frame)
importFrom(stats,predict)
importFrom(stats,setNames)
Expand Down
2 changes: 1 addition & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Applr 1.0.0 (unreleased)
# Applr 1.0.0

First stable release. The public API documented here is covered by a
stability promise: renames or removals after 1.0 go through a deprecation
Expand Down
20 changes: 10 additions & 10 deletions R/Applr-package.R
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,28 @@
#' - ggplot2 labeling: \code{\link{geom_slice_text}}, \code{\link{geom_slice_subtitle}}, \code{\link{geom_slice_caption}}
#' - 3D visualization: \code{\link{scatter_3d}}
#' - Equations: \code{\link{lm_equation}}, \code{\link{lm_latex}}
#' - 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
#' }
#'
Expand Down
41 changes: 20 additions & 21 deletions R/autoplot.lm.R
Original file line number Diff line number Diff line change
Expand Up @@ -71,30 +71,28 @@ ggplot2::autoplot
#' @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")
#' }
#'
#' @export
Expand Down Expand Up @@ -244,10 +242,11 @@ autoplot.lm <- function(object, mapping = NULL, type = c("2d", "3d"),

plot <- ggplot(model_data, plot_mapping) +
geom_point() +
geom_slice(object, interval = interval, ...) +
geom_slice_subtitle()
geom_slice(object, interval = interval, ...)
# The facet must come before the subtitle, which only skips facet variables
# already on the plot when it is added
if (!is.null(facet_var)) {
plot <- plot + facet_wrap(vars(!!as.name(facet_var)))
}
plot
plot + geom_slice_subtitle()
}
2 changes: 2 additions & 0 deletions R/deprecated.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#' ribbons, back-transformation) plus grouping, faceting, and held-variable
#' reporting. Calling it is an error that points to the replacement.
#'
#' @return None; always throws an error pointing to [geom_slice()].
#'
#' @param ... Ignored; accepted only so old calls reach the error message.
#'
#' @seealso [geom_slice()]
Expand Down
3 changes: 3 additions & 0 deletions R/diagnose.R
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
#'
#' @return None. The function is called for its side effect of producing plots.
#'
#' @importFrom graphics abline text
#' @importFrom stats lowess
#'
#' @export
#'
#' @examples
Expand Down
71 changes: 37 additions & 34 deletions R/geom_slice.R
Original file line number Diff line number Diff line change
Expand Up @@ -1032,69 +1032,72 @@ ggplot_add.SliceLayer <- function(object, plot, ...) {
#' @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)
#'
#' @export
geom_slice <- function(model,
Expand Down
10 changes: 6 additions & 4 deletions R/geom_slice_caption.R
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,13 @@ ggplot_add.slice_caption_spec <- function(object, plot, ...) {
#' @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()
#'
#' @export
Expand Down
10 changes: 6 additions & 4 deletions R/geom_slice_subtitle.R
Original file line number Diff line number Diff line change
Expand Up @@ -245,11 +245,13 @@ ggplot_add.slice_subtitle_spec <- function(object, plot, ...) {
#' @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()
#'
#' @export
Expand Down
Loading
Loading