diff --git a/NAMESPACE b/NAMESPACE index b6fdbd33..50bd3625 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -32,6 +32,7 @@ export(type_qq) export(type_rect) export(type_ribbon) export(type_ridge) +export(type_rug) export(type_segments) export(type_spineplot) export(type_spline) @@ -81,6 +82,7 @@ importFrom(graphics,polygon) importFrom(graphics,polypath) importFrom(graphics,rasterImage) importFrom(graphics,rect) +importFrom(graphics,rug) importFrom(graphics,segments) importFrom(graphics,strwidth) importFrom(graphics,text) diff --git a/NEWS.md b/NEWS.md index b8eee933..e5300486 100644 --- a/NEWS.md +++ b/NEWS.md @@ -58,6 +58,8 @@ New plot types: @vincentarelbundock) - `type_ridge()` (shortcut: `"ridge"`) for ridge plots aka Joy plots. (#252 @vincentarelbundock, @zeileis, and @grantmcdermott) + - `type_rug()` (shortcut: `"rug"`) adds a rug to an existing plot. (#276 + @grantmcdermott) - Models: - `type_glm()` (shortcut: `"glm"`) (@vincentarelbundock) - `type_lm()` (shortcut: `"lm"`) (@vincentarelbundock) diff --git a/R/sanitize.R b/R/sanitize.R index 3fe7a193..8202b5bd 100644 --- a/R/sanitize.R +++ b/R/sanitize.R @@ -16,7 +16,7 @@ sanitize_type = function(type, x, y, dots) { "density", "abline", "area", "boxplot", "errorbar", "function", "glm", "hist", "histogram", "hline", "j", "jitter", "lines", "lm", "loess", "pointrange", - "points", "polygon", "polypath", "qq", "rect", "ribbon", "ridge", + "points", "polygon", "polypath", "qq", "rect", "ribbon", "ridge", "rug", "segments", "spineplot", "spline", "vline" ) assert_choice(type, types, null.ok = TRUE) @@ -56,6 +56,7 @@ sanitize_type = function(type, x, y, dots) { "rect" = type_rect, "ribbon" = type_ribbon, "ridge" = type_ridge, + "rug" = type_rug, "segments" = type_segments, "spineplot" = type_spineplot, "spline" = type_spline, diff --git a/R/tinyplot.R b/R/tinyplot.R index 4f9ad617..fede03eb 100644 --- a/R/tinyplot.R +++ b/R/tinyplot.R @@ -107,6 +107,7 @@ #' - `"jitter"` / [`type_jitter()`]: Jittered points. #' - `"qq"` / [`type_qq()`]: Creates a quantile-quantile plot. #' - `"ridge"` / [`type_ridge()`]: Creates a ridgeline (aka joy) plot. +#' - `"rug"` / [`type_rug()`]: Adds a rug to an existing plot. #' - `"spineplot"` / [`type_spineplot()`]: Creates a spineplot or spinogram. #' - Models: #' - `"loess"` / [`type_loess()`]: Local regression curve. diff --git a/R/type_rug.R b/R/type_rug.R new file mode 100644 index 00000000..d9ab47db --- /dev/null +++ b/R/type_rug.R @@ -0,0 +1,68 @@ +#' Add a rug to a plot +#' +#' @description +#' Adds a rug representation (1-d plot) of the data to the plot. +#' +#' @details +#' This function should only be used as part of [`tinyplot_add()`], i.e. adding +#' to an existing plot. +#' +#' In most cases, determining which variable receives the rug representation +#' will be based on the `side` argument (i.e., x-variable if side is 1 or 3, and +#' y-variable if side is 2 or 4). An exception is if the preceding plot type was +#' either `"density"` or `"histogram"`; for these latter cases, the x-variable +#' will always be used. See Examples. +#' +#' @inheritParams graphics::rug +#' @param jitter Logical. Add jittering to separate ties? Default is `FALSE`. +#' @param amount Numeric. Amount of jittering (see \code{\link[base]{jitter}}). +#' Only used if `jitter` is `TRUE`. +#' @examples +#' tinyplot(~wt | am, data = mtcars, type = "density", facet = "by", fill = "by") +#' tinyplot_add(type = "rug") +#' # use type_rug() to pass extra options +#' tinyplot_add(type = type_rug(side = 3, ticksize = 0.05)) +#' +#' # For ties, use jittering +#' tinyplot(eruptions ~ waiting, data = faithful, type = "lm") +#' tinyplot_add(type = type_rug(jitter = TRUE, amount = 0.3)) +#' tinyplot_add(type = type_rug(jitter = TRUE, amount = 0.1, side = 2)) +#' # Add original points just for reference +#' tinyplot_add(type = "p") +#' +#' @importFrom graphics rug +#' @export +type_rug = function(ticksize = 0.03, side = 1, quiet = getOption("warn") < 0, jitter = FALSE, amount = NULL) { + data_rug = function(datapoints, ...) { + if (nrow(datapoints) == 0) { + msg = "`type_rug() only works on existing plots with x and y data points." + stop(msg, call. = FALSE) + } + return(datapoints) + } + draw_rug = function(.ticksize = ticksize, .side = side, .quiet = quiet, .jitter = jitter, .amount = amount) { + fun = function(ix, iy, icol, ilwd, ...) { + lc = getOption("tinyplot_last_call", default = NULL) + swapy = !is.null(lc$type) && lc$type %in% c("density", "hist", "histogram") + rugx = if (swapy) iy else if (side %in% c(1, 3)) ix else iy + if (isTRUE(jitter)) rugx = jitter(rugx, amount = .amount) + rug( + x = rugx, + col = icol, + lwd = ilwd, + ticksize = .ticksize, + side = .side, + quiet = .quiet + ) + } + return(fun) + } + + out = list( + draw = draw_rug(), + data = data_rug, + name = "rug" + ) + class(out) = "tinyplot_type" + return(out) +} diff --git a/altdoc/quarto_website.yml b/altdoc/quarto_website.yml index 4661ef73..290bc87c 100644 --- a/altdoc/quarto_website.yml +++ b/altdoc/quarto_website.yml @@ -73,6 +73,8 @@ website: file: man/type_qq.qmd - text: type_ridge file: man/type_ridge.qmd + - text: type_rug + file: man/type_rug.qmd - text: type_spineplot file: man/type_spineplot.qmd - section: Models diff --git a/inst/tinytest/_tinysnapshot/tinyplot_add_rug.svg b/inst/tinytest/_tinysnapshot/tinyplot_add_rug.svg new file mode 100644 index 00000000..4a444143 --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinyplot_add_rug.svg @@ -0,0 +1,603 @@ + + + + + + + + + + + + +waiting +eruptions + + + + + + +50 +60 +70 +80 +90 + + + + + +2 +3 +4 +5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/_tinysnapshot/tinyplot_add_rug_density.svg b/inst/tinytest/_tinysnapshot/tinyplot_add_rug_density.svg new file mode 100644 index 00000000..02333e7f --- /dev/null +++ b/inst/tinytest/_tinysnapshot/tinyplot_add_rug_density.svg @@ -0,0 +1,336 @@ + + + + + + + + + + + + +N = 272 Bandwidth = 0.3348 +Density + + + + + + + +1 +2 +3 +4 +5 +6 + + + + + + + +0.0 +0.1 +0.2 +0.3 +0.4 +0.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inst/tinytest/test-tinyplot_add.R b/inst/tinytest/test-tinyplot_add.R index 4e8addc6..134d6030 100644 --- a/inst/tinytest/test-tinyplot_add.R +++ b/inst/tinytest/test-tinyplot_add.R @@ -37,3 +37,18 @@ f = function() { tinyplot_add(mpg ~ hp | factor(cyl), type = "p", pch = 16) } expect_snapshot_plot(f, label = "tinyplot_add_unnamed") + +# type = "rug" +f = function() { + tinyplot(eruptions ~ waiting, data = faithful, type = "lm") + tinyplot_add(type = "rug") + tinyplot_add(type = type_rug(side = 2, jitter = TRUE, amount = 0.1)) +} +expect_snapshot_plot(f, label = "tinyplot_add_rug") + +# type = "rug" (adding to "density" should default to x variable) +f = function() { + tinyplot( ~ eruptions, data = faithful, type = "density") + tinyplot_add(type = "rug") +} +expect_snapshot_plot(f, label = "tinyplot_add_rug_density") \ No newline at end of file diff --git a/man/tinyplot.Rd b/man/tinyplot.Rd index 666868e2..0941375f 100644 --- a/man/tinyplot.Rd +++ b/man/tinyplot.Rd @@ -233,6 +233,7 @@ type of plot desired. \item \code{"jitter"} / \code{\link[=type_jitter]{type_jitter()}}: Jittered points. \item \code{"qq"} / \code{\link[=type_qq]{type_qq()}}: Creates a quantile-quantile plot. \item \code{"ridge"} / \code{\link[=type_ridge]{type_ridge()}}: Creates a ridgeline (aka joy) plot. +\item \code{"rug"} / \code{\link[=type_rug]{type_rug()}}: Adds a rug to an existing plot. \item \code{"spineplot"} / \code{\link[=type_spineplot]{type_spineplot()}}: Creates a spineplot or spinogram. } \item Models: diff --git a/man/type_rug.Rd b/man/type_rug.Rd new file mode 100644 index 00000000..777092d6 --- /dev/null +++ b/man/type_rug.Rd @@ -0,0 +1,56 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/type_rug.R +\name{type_rug} +\alias{type_rug} +\title{Add a rug to a plot} +\usage{ +type_rug( + ticksize = 0.03, + side = 1, + quiet = getOption("warn") < 0, + jitter = FALSE, + amount = NULL +) +} +\arguments{ +\item{ticksize}{The length of the ticks making up the \sQuote{rug}. + Positive lengths give inwards ticks.} + +\item{side}{On which side of the plot box the rug will be + plotted. Normally 1 (bottom) or 3 (top).} + +\item{quiet}{logical indicating if there should be a warning about + clipped values.} + +\item{jitter}{Logical. Add jittering to separate ties? Default is \code{FALSE}.} + +\item{amount}{Numeric. Amount of jittering (see \code{\link[base]{jitter}}). +Only used if \code{jitter} is \code{TRUE}.} +} +\description{ +Adds a rug representation (1-d plot) of the data to the plot. +} +\details{ +This function should only be used as part of \code{\link[=tinyplot_add]{tinyplot_add()}}, i.e. adding +to an existing plot. + +In most cases, determining which variable receives the rug representation +will be based on the \code{side} argument (i.e., x-variable if side is 1 or 3, and +y-variable if side is 2 or 4). An exception is if the preceding plot type was +either \code{"density"} or \code{"histogram"}; for these latter cases, the x-variable +will always be used. See Examples. +} +\examples{ +tinyplot(~wt | am, data = mtcars, type = "density", facet = "by", fill = "by") +tinyplot_add(type = "rug") +# use type_rug() to pass extra options +tinyplot_add(type = type_rug(side = 3, ticksize = 0.05)) + +# For ties, use jittering +tinyplot(eruptions ~ waiting, data = faithful, type = "lm") +tinyplot_add(type = type_rug(jitter = TRUE, amount = 0.3)) +tinyplot_add(type = type_rug(jitter = TRUE, amount = 0.1, side = 2)) +# Add original points just for reference +tinyplot_add(type = "p") + +} diff --git a/vignettes/types.qmd b/vignettes/types.qmd index 932c5f28..78533423 100644 --- a/vignettes/types.qmd +++ b/vignettes/types.qmd @@ -73,6 +73,7 @@ Visualizations: - `"jitter"` / `type_jitter()`: Jittered points. - `"qq"` / `type_qq()`: Creates a quantile-quantile plot. - `"ridge"` / `type_ridge()`: Creates a ridgeline (aka joy) plot. +- `"rug"` / `type_rug()`: Adds a rug to an existing plot. - `"spineplot"` / `type_spineplot()`: Creates a spineplot or spinogram. Models: