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 @@
+
+
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 @@
+
+
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: