Skip to content

Commit b3cc671

Browse files
authored
tinyplot() methods (#533)
* `tinyplot()` methods Fixes #532 * typo * add skeleton * add to namespace * fix * fix * fix * Update tinyplot.R * fix * Update tinyplot.R * Update tinyplot.R * typo * ... * simplify * ... * use formulas * fix * fix * fix * add methods and examples * simplify, add labs * fix * fix example * no support for joint dots * wordlist * theming * show_data
1 parent 415773c commit b3cc671

File tree

7 files changed

+233
-16
lines changed

7 files changed

+233
-16
lines changed

DESCRIPTION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ Suggests:
9898
see (>= 0.11.0),
9999
survival,
100100
testthat (>= 3.2.1),
101+
tinyplot,
101102
tinytable,
102103
vdiffr,
103104
withr

NAMESPACE

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ S3method(standardize,estimate_predicted)
6464
S3method(standardize,estimate_slopes)
6565
S3method(summary,estimate_slopes)
6666
S3method(summary,reshape_grouplevel)
67+
S3method(tinyplot::tinyplot,estimate_grouplevel)
68+
S3method(tinyplot::tinyplot,estimate_means)
69+
S3method(tinyplot::tinyplot,estimate_predicted)
70+
S3method(tinyplot::tinyplot,estimate_slopes)
6771
S3method(unstandardize,estimate_contrasts)
6872
S3method(unstandardize,estimate_means)
6973
S3method(unstandardize,estimate_predicted)

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Changes
44

5+
* Methods for the *tinyplot* package were added.
6+
57
* `estimate_expectation()` and `estimate_relation()` now support objects of
68
class `htest`.
79

R/tinyplot.R

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
#' @rdname visualisation_recipe.estimate_predicted
2+
#' @param theme A character string specifying the theme to use for the plot.
3+
#' Defaults to `"tufte"`. For other options please see [`tinyplot::tinytheme()`].
4+
#' Use `NULL` if no theme should be applied.
5+
#'
6+
#' @examplesIf all(insight::check_if_installed(c("tinyplot", "marginaleffects"), quietly = TRUE))
7+
#' # ==============================================
8+
#' # tinyplot
9+
#' # ==============================================
10+
#' \donttest{
11+
#' data(efc, package = "modelbased")
12+
#' efc <- datawizard::to_factor(efc, c("e16sex", "c172code", "e42dep"))
13+
#' m <- lm(neg_c_7 ~ e16sex + c172code + barthtot, data = efc)
14+
#'
15+
#' em <- estimate_means(m, "c172code")
16+
#' tinyplot::plt(em)
17+
#'
18+
#' em <- estimate_means(m, "barthtot")
19+
#' tinyplot::plt(em)
20+
#'
21+
#' m <- lm(neg_c_7 ~ e16sex * c172code + e42dep, data = efc)
22+
#' em <- estimate_means(m, c("e16sex", "c172code"))
23+
#' tinyplot::plt(em)
24+
#' }
25+
#' @exportS3Method tinyplot::tinyplot
26+
tinyplot.estimate_means <- function(
27+
x,
28+
show_data = FALSE,
29+
numeric_as_discrete = NULL,
30+
theme = "tufte",
31+
...
32+
) {
33+
insight::check_if_installed("tinyplot")
34+
35+
# init --------------------------------------------------
36+
response_scale <- attributes(x)$predict
37+
model_info <- attributes(x)$model_info
38+
39+
# set defaults
40+
if (is.null(numeric_as_discrete)) {
41+
numeric_as_discrete <- getOption("modelbased_numeric_as_discrete", 8)
42+
}
43+
44+
# we re-use the ggplot function here to retrieve the aesthetics and data. we
45+
# now need to extract the aesthetics and data and use it to create a tinyplot
46+
# object
47+
aes <- .find_aes(x, model_info, numeric_as_discrete)
48+
data <- aes$data
49+
aes <- aes$aes
50+
51+
# save additional arguments, once for theming and once for the plot
52+
dots <- list(...)
53+
theme_dots <- dots
54+
55+
# preparation of settings / arguments ----------------------------------
56+
57+
# Don't plot raw data if `predict` is not on the response scale
58+
if (
59+
!is.null(response_scale) &&
60+
!response_scale %in% c("prediction", "response", "expectation", "invlink(link)")
61+
) {
62+
show_data <- FALSE
63+
}
64+
65+
# Don't plot raw data for transformed responses with no back-transformation
66+
transform <- attributes(x)$transform
67+
68+
if (isTRUE(model_info$is_linear) && !isTRUE(transform)) {
69+
# add information about response transformation
70+
trans_fun <- .safe(insight::find_transformation(attributes(x)$model))
71+
if (!is.null(trans_fun) && all(trans_fun != "identity")) {
72+
show_data <- FALSE
73+
}
74+
}
75+
76+
# handle non-standard plot types -------------------------------
77+
78+
if (aes$type == "grouplevel") {
79+
aes$type <- "pointrange"
80+
dots$flip <- TRUE
81+
}
82+
83+
# base elements as formula for tinyplot -------------------------------
84+
85+
# plot formula
86+
if (is.null(aes$color)) {
87+
plot_formula <- paste(aes$y, "~", aes$x)
88+
} else {
89+
plot_formula <- paste(aes$y, "~", aes$x, "|", aes$color)
90+
}
91+
plot_description <- stats::as.formula(plot_formula)
92+
93+
# facets, also as formula
94+
if (is.null(dots$facet) && !is.null(aes$facet)) {
95+
dots$facet <- stats::as.formula(paste("~", aes$facet, collapse = " + "))
96+
}
97+
98+
# add remaining aesthetics to the plot description as symbols
99+
elements <- c("xmin", "xmax", "ymin", "ymax")
100+
plot_args <- lapply(elements, function(el) {
101+
if (is.null(aes[[el]])) {
102+
return(NULL)
103+
}
104+
as.symbol(aes[[el]])
105+
})
106+
names(plot_args) <- elements
107+
108+
## TODO: legend labels?
109+
110+
# x/y labels --------------------------------
111+
dots$xlab <- aes$labs$x
112+
dots$ylab <- aes$labs$y
113+
114+
# add aesthetics to the plot description
115+
plot_args <- insight::compact_list(c(
116+
list(plot_description, data = data, type = aes$type),
117+
plot_args,
118+
dots
119+
))
120+
121+
# default theme
122+
if (!is.null(theme)) {
123+
theme_dots[c(elements, "facet", "xlab", "ylab", "flip")] <- NULL
124+
do.call(tinyplot::tinytheme, c(list(theme = theme), theme_dots))
125+
}
126+
127+
# add data points if requested --------------------------------
128+
129+
if (show_data) {
130+
# extract raw data from the model
131+
model <- attributes(x)$model
132+
rawdata <- as.data.frame(insight::get_data(model, verbose = FALSE))
133+
134+
# set alpha
135+
if (is.null(dots$alpha)) {
136+
dots$alpha <- 0.3
137+
}
138+
139+
# add layer
140+
plot_args$draw <- {
141+
tinyplot::tinyplot(
142+
# we need the original response name for the data points
143+
# so we update the formula for the plot description
144+
stats::reformulate(
145+
attr(stats::terms(plot_description), "term.labels"),
146+
response = insight::find_response(model)
147+
),
148+
data = rawdata,
149+
facet = dots$facet,
150+
type = "jitter",
151+
add = TRUE,
152+
alpha = dots$alpha
153+
)
154+
}
155+
}
156+
157+
# plot it!
158+
do.call(tinyplot::tinyplot, plot_args)
159+
}
160+
161+
#' @exportS3Method tinyplot::tinyplot
162+
tinyplot.estimate_predicted <- tinyplot.estimate_means
163+
164+
#' @exportS3Method tinyplot::tinyplot
165+
tinyplot.estimate_slopes <- tinyplot.estimate_means
166+
167+
#' @exportS3Method tinyplot::tinyplot
168+
tinyplot.estimate_grouplevel <- tinyplot.estimate_means

R/visualisation_recipe.R

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
#' @name visualisation_recipe.estimate_predicted
33
#'
44
#' @description
5-
#' Most **modelbased** objects can be visualized using the `plot()` function,
6-
#' which internally calls the `visualisation_recipe()` function. See the
5+
#' Most **modelbased** objects can be visualized using either the `plot()`
6+
#' function, which internally calls the `visualisation_recipe()` function and
7+
#' relies on `{ggplot2}`. There is also a `tinyplot()` method, which uses the
8+
#' `{tinyplot}` package and relies on the core R graphic system. See the
79
#' examples below for more information and examples on how to create and
810
#' customize plots.
911
#'
@@ -39,7 +41,8 @@
3941
#' e.g. `options(modelbased_numeric_as_discrete = 10)`.
4042
#' @param point,line,pointrange,ribbon,facet,grid Additional
4143
#' aesthetics and parameters for the geoms (see customization example).
42-
#' @param ... Arguments passed from `plot()` to `visualisation_recipe()`.
44+
#' @param ... Arguments passed from `plot()` to `visualisation_recipe()`, or
45+
#' to `tinyplot()` and `tinytheme()` if you use that method.
4346
#'
4447
#' @details There are two options to remove the confidence bands or errors bars
4548
#' from the plot. To remove error bars, simply set the `pointrange` geom to

inst/WORDLIST

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,6 @@ visualisation
190190
visualise
191191
visualising
192192
walkthrough
193+
Nonresponse
194+
tinyplot
195+

man/visualisation_recipe.estimate_predicted.Rd

Lines changed: 49 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)