diff --git a/NEWS.md b/NEWS.md
index 140d7dd25b..3d9a9f7f99 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -51,6 +51,13 @@
(@teunbrand, #4320)
* `geom_boxplot()` gains additional arguments to style the colour, linetype and
linewidths of the box, whiskers, median line and staples (@teunbrand, #5126)
+* `geom_violin()` gains additional arguments to style the colour, linetype and
+ linewidths of the quantiles, which replace the now-deprecated `draw_quantiles`
+ argument (#5912).
+* (breaking) `geom_violin(quantiles)` now has actual quantiles based on
+ the data, rather than inferred quantiles based on the computed density. The
+ `quantiles` parameter that replaces `draw_quantiles` now belongs to
+ `stat_ydensity()` instead of `geom_violin()` (@teunbrand, #4120).
* (internal) Using `after_scale()` in the `Geom*$default_aes()` field is now
evaluated in the context of data (@teunbrand, #6135)
* Fixed bug where binned scales wouldn't simultaneously accept transformations
diff --git a/R/geom-violin.R b/R/geom-violin.R
index e53c29396f..8c3d23ad71 100644
--- a/R/geom-violin.R
+++ b/R/geom-violin.R
@@ -10,8 +10,6 @@
#' @eval rd_aesthetics("geom", "violin")
#' @inheritParams layer
#' @inheritParams geom_bar
-#' @param draw_quantiles If `not(NULL)` (default), draw horizontal lines
-#' at the given quantiles of the density estimate.
#' @param trim If `TRUE` (default), trim the tails of the violins
#' to the range of the data. If `FALSE`, don't trim the tails.
#' @param geom,stat Use to override the default connection between
@@ -23,6 +21,12 @@
#' finite, boundary effect of default density estimation will be corrected by
#' reflecting tails outside `bounds` around their closest edge. Data points
#' outside of bounds are removed with a warning.
+#' @param quantile.colour,quantile.color,quantile.linewidth,quantile.linetype
+#' Default aesthetics for the quantile lines. Set to `NULL` to inherit from
+#' the data's aesthetics. By default, quantile lines are hidden and can be
+#' turned on by changing `quantile.linetype`.
+#' @param draw_quantiles `r lifecycle::badge("deprecated")` Previous
+#' specification of drawing quantiles.
#' @export
#' @references Hintze, J. L., Nelson, R. D. (1998) Violin Plots: A Box
#' Plot-Density Trace Synergism. The American Statistician 52, 181-184.
@@ -91,14 +95,46 @@
geom_violin <- function(mapping = NULL, data = NULL,
stat = "ydensity", position = "dodge",
...,
- draw_quantiles = NULL,
trim = TRUE,
bounds = c(-Inf, Inf),
+ quantile.colour = NULL,
+ quantile.color = NULL,
+ quantile.linetype = 0L,
+ quantile.linewidth = NULL,
+ draw_quantiles = deprecated(),
scale = "area",
na.rm = FALSE,
orientation = NA,
show.legend = NA,
inherit.aes = TRUE) {
+
+ extra <- list()
+ if (lifecycle::is_present(draw_quantiles)) {
+ deprecate_soft0(
+ "3.6.0",
+ what = "geom_violin(draw_quantiles)",
+ with = "geom_violin(quantiles.linetype)"
+ )
+ check_numeric(draw_quantiles)
+
+ # Pass on to stat when stat accepts 'quantiles'
+ stat <- check_subclass(stat, "Stat", current_call(), caller_env())
+ if ("quantiles" %in% stat$parameters()) {
+ extra$quantiles <- draw_quantiles
+ }
+
+ # Turn on quantile lines
+ if (!is.null(quantile.linetype)) {
+ quantile.linetype <- max(quantile.linetype, 1)
+ }
+ }
+
+ quantile_gp <- list(
+ colour = quantile.color %||% quantile.colour,
+ linetype = quantile.linetype,
+ linewidth = quantile.linewidth
+ )
+
layer(
data = data,
mapping = mapping,
@@ -110,10 +146,11 @@ geom_violin <- function(mapping = NULL, data = NULL,
params = list2(
trim = trim,
scale = scale,
- draw_quantiles = draw_quantiles,
na.rm = na.rm,
orientation = orientation,
bounds = bounds,
+ quantile_gp = quantile_gp,
+ !!!extra,
...
)
)
@@ -146,7 +183,7 @@ GeomViolin <- ggproto("GeomViolin", Geom,
flip_data(data, params$flipped_aes)
},
- draw_group = function(self, data, ..., draw_quantiles = NULL, flipped_aes = FALSE) {
+ draw_group = function(self, data, ..., quantile_gp = list(linetype = 0), flipped_aes = FALSE) {
data <- flip_data(data, flipped_aes)
# Find the points for the line to go all the way around
data <- transform(data,
@@ -165,36 +202,28 @@ GeomViolin <- ggproto("GeomViolin", Geom,
newdata <- vec_rbind0(newdata, newdata[1,])
newdata <- flip_data(newdata, flipped_aes)
+ violin_grob <- GeomPolygon$draw_panel(newdata, ...)
+
+ if (!"quantile" %in% names(newdata) ||
+ all(quantile_gp$linetype == 0) ||
+ all(quantile_gp$linetype == "blank")) {
+ return(ggname("geom_violin", violin_grob))
+ }
+
# Draw quantiles if requested, so long as there is non-zero y range
- if (length(draw_quantiles) > 0 & !scales::zero_range(range(data$y))) {
- if (!(all(draw_quantiles >= 0) && all(draw_quantiles <= 1))) {
- cli::cli_abort("{.arg draw_quantiles} must be between 0 and 1.")
- }
-
- # Compute the quantile segments and combine with existing aesthetics
- quantiles <- create_quantile_segment_frame(data, draw_quantiles)
- aesthetics <- data[
- rep(1, nrow(quantiles)),
- setdiff(names(data), c("x", "y", "group")),
- drop = FALSE
- ]
- aesthetics$alpha <- rep(1, nrow(quantiles))
- both <- vec_cbind(quantiles, aesthetics)
- both <- both[!is.na(both$group), , drop = FALSE]
- both <- flip_data(both, flipped_aes)
- quantile_grob <- if (nrow(both) == 0) {
- zeroGrob()
- } else {
- GeomPath$draw_panel(both, ...)
- }
-
- ggname("geom_violin", grobTree(
- GeomPolygon$draw_panel(newdata, ...),
- quantile_grob)
- )
+ quantiles <- newdata[!is.na(newdata$quantile),]
+ quantiles$group <- match(quantiles$quantile, unique(quantiles$quantile))
+ quantiles$linetype <- quantile_gp$linetype %||% quantiles$linetype
+ quantiles$linewidth <- quantile_gp$linewidth %||% quantiles$linewidth
+ quantiles$colour <- quantile_gp$colour %||% quantiles$colour
+
+ quantile_grob <- if (nrow(quantiles) == 0) {
+ zeroGrob()
} else {
- ggname("geom_violin", GeomPolygon$draw_panel(newdata, ...))
+ GeomPath$draw_panel(quantiles, ...)
}
+
+ ggname("geom_violin", grobTree(violin_grob, quantile_grob))
},
draw_key = draw_key_polygon,
diff --git a/R/stat-ydensity.R b/R/stat-ydensity.R
index 4eadd8ca58..6b0e4f0ff8 100644
--- a/R/stat-ydensity.R
+++ b/R/stat-ydensity.R
@@ -7,6 +7,8 @@
#' @param drop Whether to discard groups with less than 2 observations
#' (`TRUE`, default) or keep such groups for position adjustment purposes
#' (`FALSE`).
+#' @param quantiles If not `NULL` (default), compute the `quantile` variable
+#' and draw horizontal lines at the given quantiles in `geom_violin()`.
#'
#' @eval rd_computed_vars(
#' density = "Density estimate.",
@@ -16,7 +18,8 @@
#' violinwidth = "Density scaled for the violin plot, according to area,
#' counts or to a constant maximum width.",
#' n = "Number of points.",
-#' width = "Width of violin bounding box."
+#' width = "Width of violin bounding box.",
+#' quantile = "Whether the row is part of the `quantiles` computation."
#' )
#'
#' @seealso [geom_violin()] for examples, and [stat_density()]
@@ -26,6 +29,7 @@
stat_ydensity <- function(mapping = NULL, data = NULL,
geom = "violin", position = "dodge",
...,
+ quantiles = c(0.25, 0.50, 0.75),
bw = "nrd0",
adjust = 1,
kernel = "gaussian",
@@ -56,6 +60,7 @@ stat_ydensity <- function(mapping = NULL, data = NULL,
drop = drop,
na.rm = na.rm,
bounds = bounds,
+ quantiles = quantiles,
...
)
)
@@ -73,14 +78,26 @@ StatYdensity <- ggproto("StatYdensity", Stat,
setup_params = function(data, params) {
params$flipped_aes <- has_flipped_aes(data, params, main_is_orthogonal = TRUE, group_has_equal = TRUE)
+ if (!is.null(params$draw_quantiles)) {
+ deprecate_soft0(
+ "3.6.0",
+ what = "stat_ydensity(draw_quantiles)",
+ with = "stat_ydensity(quantiles)"
+ )
+ params$quantiles <- params$draw_quantiles
+ check_numeric(params$quantiles, arg = "quantiles")
+ }
+
params
},
- extra_params = c("na.rm", "orientation"),
+ # `draw_quantiles` is here for deprecation repair reasons
+ extra_params = c("na.rm", "orientation", "draw_quantiles"),
compute_group = function(self, data, scales, width = NULL, bw = "nrd0", adjust = 1,
kernel = "gaussian", trim = TRUE, na.rm = FALSE,
- drop = TRUE, flipped_aes = FALSE, bounds = c(-Inf, Inf)) {
+ drop = TRUE, flipped_aes = FALSE, bounds = c(-Inf, Inf),
+ quantiles = c(0.25, 0.50, 0.75)) {
if (nrow(data) < 2) {
if (isTRUE(drop)) {
cli::cli_warn(c(
@@ -115,17 +132,43 @@ StatYdensity <- ggproto("StatYdensity", Stat,
}
dens$width <- width
+ if (!is.null(quantiles)) {
+ if (!(all(quantiles >= 0) && all(quantiles <= 1))) {
+ cli::cli_abort("{.arg quantiles} must be between 0 and 1.")
+ }
+ if (!is.null(data[["weight"]]) || !all(data[["weight"]] == 1)) {
+ cli::cli_warn(
+ "{.arg quantiles} for weighted data is not implemented."
+ )
+ }
+ quants <- quantile(data$y, probs = quantiles)
+ quants <- data_frame0(
+ y = unname(quants),
+ quantile = quantiles
+ )
+
+ # Interpolate other metrics
+ for (var in setdiff(names(dens), names(quants))) {
+ quants[[var]] <-
+ approx(dens$y, dens[[var]], xout = quants$y, ties = "ordered")$y
+ }
+
+ dens <- vec_slice(dens, !dens$y %in% quants$y)
+ dens <- vec_c(dens, quants)
+ }
+
dens
},
compute_panel = function(self, data, scales, width = NULL, bw = "nrd0", adjust = 1,
kernel = "gaussian", trim = TRUE, na.rm = FALSE,
scale = "area", flipped_aes = FALSE, drop = TRUE,
- bounds = c(-Inf, Inf)) {
+ bounds = c(-Inf, Inf), quantiles = c(0.25, 0.50, 0.75)) {
data <- flip_data(data, flipped_aes)
data <- ggproto_parent(Stat, self)$compute_panel(
data, scales, width = width, bw = bw, adjust = adjust, kernel = kernel,
trim = trim, na.rm = na.rm, drop = drop, bounds = bounds,
+ quantiles = quantiles
)
if (!drop && any(data$n < 2)) {
cli::cli_warn(
diff --git a/man/geom_violin.Rd b/man/geom_violin.Rd
index 974d1c5bdc..244a7ac7ea 100644
--- a/man/geom_violin.Rd
+++ b/man/geom_violin.Rd
@@ -11,9 +11,13 @@ geom_violin(
stat = "ydensity",
position = "dodge",
...,
- draw_quantiles = NULL,
trim = TRUE,
bounds = c(-Inf, Inf),
+ quantile.colour = NULL,
+ quantile.color = NULL,
+ quantile.linetype = 0L,
+ quantile.linewidth = NULL,
+ draw_quantiles = deprecated(),
scale = "area",
na.rm = FALSE,
orientation = NA,
@@ -27,6 +31,7 @@ stat_ydensity(
geom = "violin",
position = "dodge",
...,
+ quantiles = c(0.25, 0.5, 0.75),
bw = "nrd0",
adjust = 1,
kernel = "gaussian",
@@ -102,9 +107,6 @@ lists which parameters it can accept.
\link[=draw_key]{key glyphs}, to change the display of the layer in the legend.
}}
-\item{draw_quantiles}{If \code{not(NULL)} (default), draw horizontal lines
-at the given quantiles of the density estimate.}
-
\item{trim}{If \code{TRUE} (default), trim the tails of the violins
to the range of the data. If \code{FALSE}, don't trim the tails.}
@@ -114,6 +116,13 @@ finite, boundary effect of default density estimation will be corrected by
reflecting tails outside \code{bounds} around their closest edge. Data points
outside of bounds are removed with a warning.}
+\item{quantile.colour, quantile.color, quantile.linewidth, quantile.linetype}{Default aesthetics for the quantile lines. Set to \code{NULL} to inherit from
+the data's aesthetics. By default, quantile lines are hidden and can be
+turned on by changing \code{quantile.linetype}.}
+
+\item{draw_quantiles}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Previous
+specification of drawing quantiles.}
+
\item{scale}{if "area" (default), all violins have the same area (before trimming
the tails). If "count", areas are scaled proportionally to the number of
observations. If "width", all violins have the same maximum width.}
@@ -144,6 +153,9 @@ the default plot specification, e.g. \code{\link[=borders]{borders()}}.}
overriding these connections, see how the \link[=layer_stats]{stat} and
\link[=layer_geoms]{geom} arguments work.}
+\item{quantiles}{If not \code{NULL} (default), compute the \code{quantile} variable
+and draw horizontal lines at the given quantiles in \code{geom_violin()}.}
+
\item{bw}{The smoothing bandwidth to be used.
If numeric, the standard deviation of the smoothing kernel.
If character, a rule to choose the bandwidth, as listed in
@@ -198,6 +210,7 @@ These are calculated by the 'stat' part of layers and can be accessed with \link
\item \code{after_stat(violinwidth)}\cr Density scaled for the violin plot, according to area, counts or to a constant maximum width.
\item \code{after_stat(n)}\cr Number of points.
\item \code{after_stat(width)}\cr Width of violin bounding box.
+\item \code{after_stat(quantile)}\cr Whether the row is part of the \code{quantiles} computation.
}
}
diff --git a/tests/testthat/_snaps/geom-violin.md b/tests/testthat/_snaps/geom-violin.md
index 80da5aad02..68cc4c1c5a 100644
--- a/tests/testthat/_snaps/geom-violin.md
+++ b/tests/testthat/_snaps/geom-violin.md
@@ -1,14 +1,12 @@
# quantiles fails outside 0-1 bound
- Problem while converting geom to grob.
- i Error occurred in the 1st layer.
- Caused by error in `draw_group()`:
- ! `draw_quantiles` must be between 0 and 1.
+ Computation failed in `stat_ydensity()`.
+ Caused by error in `compute_group()`:
+ ! `quantiles` must be between 0 and 1.
---
- Problem while converting geom to grob.
- i Error occurred in the 1st layer.
- Caused by error in `draw_group()`:
- ! `draw_quantiles` must be between 0 and 1.
+ Computation failed in `stat_ydensity()`.
+ Caused by error in `compute_group()`:
+ ! `quantiles` must be between 0 and 1.
diff --git a/tests/testthat/_snaps/geom-violin/basic.svg b/tests/testthat/_snaps/geom-violin/basic.svg
index 206a6b4626..16e7518c21 100644
--- a/tests/testthat/_snaps/geom-violin/basic.svg
+++ b/tests/testthat/_snaps/geom-violin/basic.svg
@@ -27,9 +27,9 @@
-
-
-
+
+
+
diff --git a/tests/testthat/_snaps/geom-violin/continuous-x-axis-many-groups-center-should-be-at-2-0.svg b/tests/testthat/_snaps/geom-violin/continuous-x-axis-many-groups-center-should-be-at-2-0.svg
index f737690144..611f73f969 100644
--- a/tests/testthat/_snaps/geom-violin/continuous-x-axis-many-groups-center-should-be-at-2-0.svg
+++ b/tests/testthat/_snaps/geom-violin/continuous-x-axis-many-groups-center-should-be-at-2-0.svg
@@ -27,7 +27,7 @@
-
+
diff --git a/tests/testthat/_snaps/geom-violin/continuous-x-axis-single-group-center-should-be-at-1-0.svg b/tests/testthat/_snaps/geom-violin/continuous-x-axis-single-group-center-should-be-at-1-0.svg
index f11a934abb..74fc5ed64e 100644
--- a/tests/testthat/_snaps/geom-violin/continuous-x-axis-single-group-center-should-be-at-1-0.svg
+++ b/tests/testthat/_snaps/geom-violin/continuous-x-axis-single-group-center-should-be-at-1-0.svg
@@ -27,7 +27,7 @@
-
+
diff --git a/tests/testthat/_snaps/geom-violin/coord-flip.svg b/tests/testthat/_snaps/geom-violin/coord-flip.svg
index 434afe96c8..59f095248a 100644
--- a/tests/testthat/_snaps/geom-violin/coord-flip.svg
+++ b/tests/testthat/_snaps/geom-violin/coord-flip.svg
@@ -27,9 +27,9 @@
-
-
-
+
+
+
diff --git a/tests/testthat/_snaps/geom-violin/coord-polar.svg b/tests/testthat/_snaps/geom-violin/coord-polar.svg
index e70e3b11f3..02ae1107df 100644
--- a/tests/testthat/_snaps/geom-violin/coord-polar.svg
+++ b/tests/testthat/_snaps/geom-violin/coord-polar.svg
@@ -36,9 +36,9 @@
-
-
-
+
+
+
A
B
C
diff --git a/tests/testthat/_snaps/geom-violin/dodging-and-coord-flip.svg b/tests/testthat/_snaps/geom-violin/dodging-and-coord-flip.svg
index 86a328e5b5..6af10a6faa 100644
--- a/tests/testthat/_snaps/geom-violin/dodging-and-coord-flip.svg
+++ b/tests/testthat/_snaps/geom-violin/dodging-and-coord-flip.svg
@@ -27,9 +27,9 @@
-
-
-
+
+
+
diff --git a/tests/testthat/_snaps/geom-violin/dodging.svg b/tests/testthat/_snaps/geom-violin/dodging.svg
index c1ccf480ce..d1d537e3b2 100644
--- a/tests/testthat/_snaps/geom-violin/dodging.svg
+++ b/tests/testthat/_snaps/geom-violin/dodging.svg
@@ -27,9 +27,9 @@
-
-
-
+
+
+
diff --git a/tests/testthat/_snaps/geom-violin/grouping-on-x-and-fill-dodge-width-0-5.svg b/tests/testthat/_snaps/geom-violin/grouping-on-x-and-fill-dodge-width-0-5.svg
index 17142781de..fcf5700ada 100644
--- a/tests/testthat/_snaps/geom-violin/grouping-on-x-and-fill-dodge-width-0-5.svg
+++ b/tests/testthat/_snaps/geom-violin/grouping-on-x-and-fill-dodge-width-0-5.svg
@@ -27,12 +27,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/tests/testthat/_snaps/geom-violin/grouping-on-x-and-fill.svg b/tests/testthat/_snaps/geom-violin/grouping-on-x-and-fill.svg
index 56049d8ef6..477f9a02c5 100644
--- a/tests/testthat/_snaps/geom-violin/grouping-on-x-and-fill.svg
+++ b/tests/testthat/_snaps/geom-violin/grouping-on-x-and-fill.svg
@@ -27,12 +27,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/tests/testthat/_snaps/geom-violin/narrower-width-5.svg b/tests/testthat/_snaps/geom-violin/narrower-width-5.svg
index d7a23e057b..d233183697 100644
--- a/tests/testthat/_snaps/geom-violin/narrower-width-5.svg
+++ b/tests/testthat/_snaps/geom-violin/narrower-width-5.svg
@@ -27,9 +27,9 @@
-
-
-
+
+
+
diff --git a/tests/testthat/_snaps/geom-violin/quantiles.svg b/tests/testthat/_snaps/geom-violin/quantiles.svg
deleted file mode 100644
index 8bec1ac1a6..0000000000
--- a/tests/testthat/_snaps/geom-violin/quantiles.svg
+++ /dev/null
@@ -1,69 +0,0 @@
-
-
diff --git a/tests/testthat/_snaps/geom-violin/scale-area-to-sample-size-c-is-smaller.svg b/tests/testthat/_snaps/geom-violin/scale-area-to-sample-size-c-is-smaller.svg
index 1c0bf845b4..ca9f1bf889 100644
--- a/tests/testthat/_snaps/geom-violin/scale-area-to-sample-size-c-is-smaller.svg
+++ b/tests/testthat/_snaps/geom-violin/scale-area-to-sample-size-c-is-smaller.svg
@@ -27,9 +27,9 @@
-
-
-
+
+
+
diff --git a/tests/testthat/_snaps/geom-violin/styled-quantiles.svg b/tests/testthat/_snaps/geom-violin/styled-quantiles.svg
new file mode 100644
index 0000000000..0b8d55329f
--- /dev/null
+++ b/tests/testthat/_snaps/geom-violin/styled-quantiles.svg
@@ -0,0 +1,69 @@
+
+
diff --git a/tests/testthat/_snaps/geom-violin/with-smaller-bandwidth-and-points.svg b/tests/testthat/_snaps/geom-violin/with-smaller-bandwidth-and-points.svg
index 1494c6bd08..3dc573d465 100644
--- a/tests/testthat/_snaps/geom-violin/with-smaller-bandwidth-and-points.svg
+++ b/tests/testthat/_snaps/geom-violin/with-smaller-bandwidth-and-points.svg
@@ -27,9 +27,9 @@
-
-
-
+
+
+
diff --git a/tests/testthat/_snaps/geom-violin/with-tails-and-points.svg b/tests/testthat/_snaps/geom-violin/with-tails-and-points.svg
index 1db22dd441..d109c20fbc 100644
--- a/tests/testthat/_snaps/geom-violin/with-tails-and-points.svg
+++ b/tests/testthat/_snaps/geom-violin/with-tails-and-points.svg
@@ -27,9 +27,9 @@
-
-
-
+
+
+
diff --git a/tests/testthat/test-geom-violin.R b/tests/testthat/test-geom-violin.R
index a93a534b40..ff3cae8de8 100644
--- a/tests/testthat/test-geom-violin.R
+++ b/tests/testthat/test-geom-violin.R
@@ -40,7 +40,8 @@ test_that("create_quantile_segment_frame functions for 3 quantiles", {
test_that("quantiles do not fail on zero-range data", {
zero.range.data <- data_frame(y = rep(1,3))
- p <- ggplot(zero.range.data) + geom_violin(aes(1, y), draw_quantiles = 0.5)
+ p <- ggplot(zero.range.data) +
+ geom_violin(aes(1, y), quantiles = 0.5, quantile.linetype = NULL)
# This should return without error and have length one
expect_length(get_layer_grob(p), 1)
@@ -48,10 +49,10 @@ test_that("quantiles do not fail on zero-range data", {
test_that("quantiles fails outside 0-1 bound", {
p <- ggplot(mtcars) +
- geom_violin(aes(as.factor(gear), mpg), draw_quantiles = c(-1, 0.5))
+ geom_violin(aes(as.factor(gear), mpg), quantiles = c(-1, 0.5))
expect_snapshot_error(ggplotGrob(p))
p <- ggplot(mtcars) +
- geom_violin(aes(as.factor(gear), mpg), draw_quantiles = c(0.5, 2))
+ geom_violin(aes(as.factor(gear), mpg), quantiles = c(0.5, 2))
expect_snapshot_error(ggplotGrob(p))
})
@@ -70,7 +71,7 @@ test_that("quantiles do not issue warning", {
data <- data_frame(x = 1, y = c(0, 0.25, 0.5, 0.75, 5))
p <- ggplot(data, aes(x = x, y = y)) +
- geom_violin(draw_quantiles = 0.5)
+ geom_violin(quantiles = 0.5, quantile.linetype = NULL)
expect_silent(plot(p))
})
@@ -116,8 +117,13 @@ test_that("geom_violin draws correctly", {
expect_doppelganger("continuous x axis, single group (center should be at 1.0)",
ggplot(dat, aes(x = as.numeric(1), y = y)) + geom_violin()
)
- expect_doppelganger("quantiles",
- ggplot(dat, aes(x=x, y=y)) + geom_violin(draw_quantiles=c(0.25,0.5,0.75))
+ expect_doppelganger("styled quantiles",
+ ggplot(dat, aes(x=x, y=y)) +
+ geom_violin(
+ quantile.colour = "red",
+ quantile.linetype = "dotted",
+ quantile.linewidth = 2
+ )
)
dat2 <- data_frame(x = rep(factor(LETTERS[1:3]), 30), y = rnorm(90), g = rep(factor(letters[5:6]), 45))
diff --git a/tests/testthat/test-stat-ydensity.R b/tests/testthat/test-stat-ydensity.R
index d43fbcc0e3..fb5d39c036 100644
--- a/tests/testthat/test-stat-ydensity.R
+++ b/tests/testthat/test-stat-ydensity.R
@@ -39,3 +39,15 @@ test_that("mapped_discrete class is preserved", {
expect_s3_class(ld$x, "mapped_discrete")
expect_equal(unique(ld$x), c(1, 3))
})
+
+test_that("quantiles are based on actual data (#4120)", {
+
+ df <- data.frame(y = 0:10)
+ q <- seq(0.1, 0.9, by = 0.1)
+
+ p <- ggplot(df, aes("X", y)) +
+ stat_ydensity(quantiles = q)
+ ld <- get_layer_data(p)
+
+ expect_equal(ld$y[!is.na(ld$quantile)], 1:9)
+})