diff --git a/DESCRIPTION b/DESCRIPTION
index cd86c2bc..8181d1f0 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -41,6 +41,7 @@ Imports:
tidyselect,
utils
Suggests:
+ ggdist,
ggfortify,
gridExtra (>= 2.2.1),
hexbin,
diff --git a/NAMESPACE b/NAMESPACE
index 614fe4e7..4823d8df 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -123,6 +123,7 @@ export(ppc_data)
export(ppc_dens)
export(ppc_dens_overlay)
export(ppc_dens_overlay_grouped)
+export(ppc_dots)
export(ppc_ecdf_overlay)
export(ppc_ecdf_overlay_grouped)
export(ppc_error_binned)
@@ -170,6 +171,7 @@ export(ppd_boxplot)
export(ppd_data)
export(ppd_dens)
export(ppd_dens_overlay)
+export(ppd_dots)
export(ppd_ecdf_overlay)
export(ppd_freqpoly)
export(ppd_freqpoly_grouped)
diff --git a/R/ppc-distributions.R b/R/ppc-distributions.R
index e05d3ac7..75bbe2b2 100644
--- a/R/ppc-distributions.R
+++ b/R/ppc-distributions.R
@@ -15,7 +15,7 @@
#' @template args-pit-ecdf
#' @param size,alpha Passed to the appropriate geom to control the appearance of
#' the predictive distributions.
-#' @param ... Currently unused.
+#' @param ... For dot plots, optional additional arguments to pass to [ggdist::stat_dots()].
#'
#' @template details-binomial
#' @template return-ggplot-or-data
@@ -28,14 +28,19 @@
#' dataset (row) in `yrep`. For these plots `yrep` should therefore
#' contain only a small number of rows. See the **Examples** section.
#' }
+#' \item{`ppc_dots()`}{
+#' A dot plot plot is displayed for `y` and each dataset (row) in `yrep`.
+#' For these plots `yrep` should therefore contain only a small number of rows.
+#' See the **Examples** section. This function requires [ggdist::stat_dots] to be installed.
+#' }
#' \item{`ppc_freqpoly_grouped()`}{
#' A separate frequency polygon is plotted for each level of a grouping
#' variable for `y` and each dataset (row) in `yrep`. For this plot
#' `yrep` should therefore contain only a small number of rows. See the
#' **Examples** section.
#' }
-#' \item{`ppc_ecdf_overlay(), ppc_dens_overlay(),
-#' ppc_ecdf_overlay_grouped(), ppc_dens_overlay_grouped()`}{
+#' \item{`ppc_ecdf_overlay()`, `ppc_dens_overlay()`,
+#' `ppc_ecdf_overlay_grouped()`, `ppc_dens_overlay_grouped()`}{
#' Kernel density or empirical CDF estimates of each dataset (row) in
#' `yrep` are overlaid, with the distribution of `y` itself on top
#' (and in a darker shade). When using `ppc_ecdf_overlay()` with discrete
@@ -80,7 +85,7 @@
#' ppc_pit_ecdf(y, yrep, prob = 0.99, plot_diff = TRUE)
#' }
#'
-#' # for ppc_hist,dens,freqpoly,boxplot definitely use a subset yrep rows so
+#' # for ppc_hist,dens,freqpoly,boxplot,dots definitely use a subset yrep rows so
#' # only a few (instead of nrow(yrep)) histograms are plotted
#' ppc_hist(y, yrep[1:8, ])
#' \donttest{
@@ -90,6 +95,9 @@
#' # wizard hat plot
#' color_scheme_set("blue")
#' ppc_dens(y, yrep[200:202, ])
+#'
+#' # dot plot
+#' ppc_dots(y, yrep[1:8, ])
#' }
#'
#' \donttest{
@@ -507,6 +515,45 @@ ppc_boxplot <-
xaxis_title(FALSE)
}
+#' @rdname PPC-distributions
+#' @export
+#' @template args-dots
+ppc_dots <-
+ function(y,
+ yrep,
+ ...,
+ binwidth = NA,
+ quantiles = NA,
+ freq = TRUE) {
+ check_ignored_arguments(..., ok_args = c("dotsize", "layout", "stackratio", "overflow"))
+
+ suggested_package("ggdist")
+
+ data <- ppc_data(y, yrep)
+
+ ggplot(data, mapping = set_hist_aes(
+ freq = freq,
+ fill = .data$is_y_label,
+ color = .data$is_y_label
+ )) +
+ ggdist::stat_dots(
+ binwidth = binwidth,
+ quantiles = quantiles,
+ ...
+ ) +
+ scale_fill_ppc() +
+ scale_color_ppc() +
+ facet_wrap_parsed("rep_label") +
+ force_axes_in_facets() +
+ bayesplot_theme_get() +
+ space_legend_keys() +
+ yaxis_text(FALSE) +
+ yaxis_title(FALSE) +
+ yaxis_ticks(FALSE) +
+ xaxis_title(FALSE) +
+ facet_text(FALSE) +
+ facet_bg(FALSE)
+ }
#' @rdname PPC-distributions
#' @export
diff --git a/R/ppd-distributions.R b/R/ppd-distributions.R
index 7c868311..83e8baef 100644
--- a/R/ppd-distributions.R
+++ b/R/ppd-distributions.R
@@ -187,6 +187,43 @@ ppd_hist <-
facet_text(FALSE)
}
+#' @rdname PPD-distributions
+#' @export
+ppd_dots <-
+ function(ypred,
+ ...,
+ binwidth = NA,
+ quantiles = NA,
+ freq = TRUE) {
+ check_ignored_arguments(..., ok_args = c("dotsize", "layout", "stackratio", "overflow"))
+
+ suggested_package("ggdist")
+
+ data <- ppd_data(ypred)
+ ggplot(data, mapping = set_hist_aes(
+ freq,
+ color = "ypred",
+ fill = "ypred"
+ )) +
+ ggdist::stat_dots(
+ binwidth = binwidth,
+ quantiles = quantiles,
+ ...
+ ) +
+ scale_color_ppd() +
+ scale_fill_ppd() +
+ bayesplot_theme_get() +
+ facet_wrap_parsed("rep_label") +
+ force_axes_in_facets() +
+ dont_expand_y_axis() +
+ legend_none() +
+ yaxis_text(FALSE) +
+ yaxis_title(FALSE) +
+ yaxis_ticks(FALSE) +
+ xaxis_title(FALSE) +
+ facet_text(FALSE)
+ }
+
#' @rdname PPD-distributions
#' @export
diff --git a/man-roxygen/args-dots.R b/man-roxygen/args-dots.R
new file mode 100644
index 00000000..4e820d1e
--- /dev/null
+++ b/man-roxygen/args-dots.R
@@ -0,0 +1,4 @@
+#' @param quantiles For dot plots, an optional integer passed to
+#' [ggdist::stat_dots()] specifying the number of quantiles to use for a
+#' quantile dot plot. If `quantiles` is `NA` (the default) then all data
+#' points are plotted.
diff --git a/man/PPC-distributions.Rd b/man/PPC-distributions.Rd
index b178dc08..628a3ad5 100644
--- a/man/PPC-distributions.Rd
+++ b/man/PPC-distributions.Rd
@@ -12,6 +12,7 @@
\alias{ppc_freqpoly}
\alias{ppc_freqpoly_grouped}
\alias{ppc_boxplot}
+\alias{ppc_dots}
\alias{ppc_violin_grouped}
\alias{ppc_pit_ecdf}
\alias{ppc_pit_ecdf_grouped}
@@ -104,6 +105,8 @@ ppc_freqpoly_grouped(
ppc_boxplot(y, yrep, ..., notch = TRUE, size = 0.5, alpha = 1)
+ppc_dots(y, yrep, ..., binwidth = NA, quantiles = NA, freq = TRUE)
+
ppc_violin_grouped(
y,
yrep,
@@ -157,7 +160,7 @@ Will be coerced to \link[base:factor]{factor} if not already a factor.
Each value in \code{group} is interpreted as the group level pertaining
to the corresponding observation.}
-\item{...}{Currently unused.}
+\item{...}{For dot plots, optional additional arguments to pass to \code{\link[ggdist:stat_dots]{ggdist::stat_dots()}}.}
\item{size, alpha}{Passed to the appropriate geom to control the appearance of
the predictive distributions.}
@@ -194,6 +197,11 @@ function.)}
\code{\link[ggplot2:geom_boxplot]{ggplot2::geom_boxplot()}}. Note: unlike \code{geom_boxplot()}, the default is
\code{notch=TRUE}.}
+\item{quantiles}{For dot plots, an optional integer passed to
+\code{\link[ggdist:stat_dots]{ggdist::stat_dots()}} specifying the number of quantiles to use for a
+quantile dot plot. If \code{quantiles} is \code{NA} (the default) then all data
+points are plotted.}
+
\item{probs}{A numeric vector passed to \code{\link[ggplot2:geom_violin]{ggplot2::geom_violin()}}'s
\code{draw_quantiles} argument to specify at which quantiles to draw
horizontal lines. Set to \code{NULL} to remove the lines.}
@@ -259,13 +267,19 @@ estimate, or box and whiskers plot is displayed for \code{y} and each
dataset (row) in \code{yrep}. For these plots \code{yrep} should therefore
contain only a small number of rows. See the \strong{Examples} section.
}
+\item{\code{ppc_dots()}}{
+A dot plot plot is displayed for \code{y} and each dataset (row) in \code{yrep}.
+For these plots \code{yrep} should therefore contain only a small number of rows.
+See the \strong{Examples} section. This function requires \link[ggdist:stat_dots]{ggdist::stat_dots} to be installed.
+}
\item{\code{ppc_freqpoly_grouped()}}{
A separate frequency polygon is plotted for each level of a grouping
variable for \code{y} and each dataset (row) in \code{yrep}. For this plot
\code{yrep} should therefore contain only a small number of rows. See the
\strong{Examples} section.
}
-\item{\verb{ppc_ecdf_overlay(), ppc_dens_overlay(), ppc_ecdf_overlay_grouped(), ppc_dens_overlay_grouped()}}{
+\item{\code{ppc_ecdf_overlay()}, \code{ppc_dens_overlay()},
+\code{ppc_ecdf_overlay_grouped()}, \code{ppc_dens_overlay_grouped()}}{
Kernel density or empirical CDF estimates of each dataset (row) in
\code{yrep} are overlaid, with the distribution of \code{y} itself on top
(and in a darker shade). When using \code{ppc_ecdf_overlay()} with discrete
@@ -306,7 +320,7 @@ ppc_pit_ecdf(y, yrep, prob = 0.99, plot_diff = FALSE)
ppc_pit_ecdf(y, yrep, prob = 0.99, plot_diff = TRUE)
}
-# for ppc_hist,dens,freqpoly,boxplot definitely use a subset yrep rows so
+# for ppc_hist,dens,freqpoly,boxplot,dots definitely use a subset yrep rows so
# only a few (instead of nrow(yrep)) histograms are plotted
ppc_hist(y, yrep[1:8, ])
\donttest{
@@ -316,6 +330,9 @@ ppc_boxplot(y, yrep[1:8, ])
# wizard hat plot
color_scheme_set("blue")
ppc_dens(y, yrep[200:202, ])
+
+# dot plot
+ppc_dots(y, yrep[1:8, ])
}
\donttest{
diff --git a/man/PPD-distributions.Rd b/man/PPD-distributions.Rd
index d74570cf..6a3c2da4 100644
--- a/man/PPD-distributions.Rd
+++ b/man/PPD-distributions.Rd
@@ -7,6 +7,7 @@
\alias{ppd_ecdf_overlay}
\alias{ppd_dens}
\alias{ppd_hist}
+\alias{ppd_dots}
\alias{ppd_freqpoly}
\alias{ppd_freqpoly_grouped}
\alias{ppd_boxplot}
@@ -39,6 +40,8 @@ ppd_dens(ypred, ..., trim = FALSE, size = 0.5, alpha = 1)
ppd_hist(ypred, ..., binwidth = NULL, bins = NULL, breaks = NULL, freq = TRUE)
+ppd_dots(ypred, ..., binwidth = NA, quantiles = NA, freq = TRUE)
+
ppd_freqpoly(
ypred,
...,
@@ -73,7 +76,7 @@ Will be coerced to \link[base:factor]{factor} if not already a factor.
Each value in \code{group} is interpreted as the group level pertaining
to the corresponding observation.}
-\item{...}{Currently unused.}
+\item{...}{For dot plots, optional additional arguments to pass to \code{\link[ggdist:stat_dots]{ggdist::stat_dots()}}.}
\item{size, alpha}{Passed to the appropriate geom to control the appearance of
the predictive distributions.}
@@ -106,6 +109,11 @@ plots the y-axis text is off by default. To view the count or density
labels on the y-axis see the \code{\link[=yaxis_text]{yaxis_text()}} convenience
function.)}
+\item{quantiles}{For dot plots, an optional integer passed to
+\code{\link[ggdist:stat_dots]{ggdist::stat_dots()}} specifying the number of quantiles to use for a
+quantile dot plot. If \code{quantiles} is \code{NA} (the default) then all data
+points are plotted.}
+
\item{notch}{For the box plot, a logical scalar passed to
\code{\link[ggplot2:geom_boxplot]{ggplot2::geom_boxplot()}}. Note: unlike \code{geom_boxplot()}, the default is
\code{notch=TRUE}.}
diff --git a/tests/testthat/_snaps/ppc-distributions/ppc-dots-binwidth.svg b/tests/testthat/_snaps/ppc-distributions/ppc-dots-binwidth.svg
new file mode 100644
index 00000000..78c9415f
--- /dev/null
+++ b/tests/testthat/_snaps/ppc-distributions/ppc-dots-binwidth.svg
@@ -0,0 +1,853 @@
+
+
diff --git a/tests/testthat/_snaps/ppc-distributions/ppc-dots-default.svg b/tests/testthat/_snaps/ppc-distributions/ppc-dots-default.svg
new file mode 100644
index 00000000..d39bf59f
--- /dev/null
+++ b/tests/testthat/_snaps/ppc-distributions/ppc-dots-default.svg
@@ -0,0 +1,1136 @@
+
+
diff --git a/tests/testthat/_snaps/ppc-distributions/ppc-dots-quantile-binwidth.svg b/tests/testthat/_snaps/ppc-distributions/ppc-dots-quantile-binwidth.svg
new file mode 100644
index 00000000..0ebf6d45
--- /dev/null
+++ b/tests/testthat/_snaps/ppc-distributions/ppc-dots-quantile-binwidth.svg
@@ -0,0 +1,704 @@
+
+
diff --git a/tests/testthat/_snaps/ppc-distributions/ppc-dots-quantile.svg b/tests/testthat/_snaps/ppc-distributions/ppc-dots-quantile.svg
new file mode 100644
index 00000000..df31be97
--- /dev/null
+++ b/tests/testthat/_snaps/ppc-distributions/ppc-dots-quantile.svg
@@ -0,0 +1,704 @@
+
+
diff --git a/tests/testthat/_snaps/ppc-distributions/ppd-dots-binwidth.svg b/tests/testthat/_snaps/ppc-distributions/ppd-dots-binwidth.svg
new file mode 100644
index 00000000..0795a172
--- /dev/null
+++ b/tests/testthat/_snaps/ppc-distributions/ppd-dots-binwidth.svg
@@ -0,0 +1,720 @@
+
+
diff --git a/tests/testthat/_snaps/ppc-distributions/ppd-dots-default.svg b/tests/testthat/_snaps/ppc-distributions/ppd-dots-default.svg
new file mode 100644
index 00000000..84684e53
--- /dev/null
+++ b/tests/testthat/_snaps/ppc-distributions/ppd-dots-default.svg
@@ -0,0 +1,1025 @@
+
+
diff --git a/tests/testthat/_snaps/ppc-distributions/ppd-dots-quantile-binwidth.svg b/tests/testthat/_snaps/ppc-distributions/ppd-dots-quantile-binwidth.svg
new file mode 100644
index 00000000..4ccf85d5
--- /dev/null
+++ b/tests/testthat/_snaps/ppc-distributions/ppd-dots-quantile-binwidth.svg
@@ -0,0 +1,639 @@
+
+
diff --git a/tests/testthat/_snaps/ppc-distributions/ppd-dots-quantile.svg b/tests/testthat/_snaps/ppc-distributions/ppd-dots-quantile.svg
new file mode 100644
index 00000000..58765580
--- /dev/null
+++ b/tests/testthat/_snaps/ppc-distributions/ppd-dots-quantile.svg
@@ -0,0 +1,643 @@
+
+
diff --git a/tests/testthat/test-ppc-distributions.R b/tests/testthat/test-ppc-distributions.R
index e544e144..6ab88d6e 100644
--- a/tests/testthat/test-ppc-distributions.R
+++ b/tests/testthat/test-ppc-distributions.R
@@ -58,6 +58,22 @@ test_that("ppc_dens,pp_hist,ppc_freqpoly,ppc_boxplot return ggplot objects", {
expect_gg(ppd_boxplot(yrep2, notch = FALSE))
})
+test_that("ppc_dots returns a ggplot object", {
+ testthat::skip_if_not_installed("ggdist")
+
+ expect_gg(ppc_dots(y, yrep[1:8, ]))
+ expect_gg(ppc_dots(y, yrep[1,, drop = FALSE], quantiles = 25))
+ expect_gg(ppc_dots(y, yrep[1:8, ], binwidth = 0.1))
+ expect_gg(ppc_dots(y2, yrep2, binwidth = 0.1, quantiles = 25))
+
+ # ppd versions
+ expect_gg(ppd_dots(yrep[1:8, ]))
+ expect_gg(ppd_dots(yrep[1,, drop = FALSE], quantiles = 25))
+ expect_gg(ppd_dots(yrep[1:8, ], binwidth = 0.1))
+ expect_gg(ppd_dots(yrep2, binwidth = 0.1, quantiles = 25))
+
+})
+
test_that("ppc_pit_ecdf, ppc_pit_ecdf_grouped returns a ggplot object", {
expect_gg(ppc_pit_ecdf(y, yrep, interpolate_adj = FALSE))
expect_gg(ppc_pit_ecdf_grouped(y, yrep, group = group, interpolate_adj = FALSE))
@@ -177,6 +193,42 @@ test_that("ppc_boxplot renders correctly", {
vdiffr::expect_doppelganger("ppd_boxplot (alpha, size)", p_custom)
})
+test_that("ppc_dots renders correctly", {
+ testthat::skip_on_cran()
+ testthat::skip_if_not_installed("vdiffr")
+ testthat::skip_if_not_installed("ggdist")
+ skip_on_r_oldrel()
+
+ p_base <- ppc_dots(vdiff_y, vdiff_yrep[1:8, ])
+ vdiffr::expect_doppelganger("ppc_dots (default)", p_base)
+
+ p_binwidth <- ppc_dots(vdiff_y, vdiff_yrep[1:8, ], binwidth = 3)
+ expect_warning(vdiffr::expect_doppelganger("ppc_dots (binwidth)", p_binwidth),
+ "The provided binwidth will cause dots to overflow the boundaries")
+
+ p_quantile <- ppc_dots(vdiff_y, vdiff_yrep[1:8, ], quantiles = 50)
+ vdiffr::expect_doppelganger("ppc_dots (quantile)", p_quantile)
+
+ p_quantile_binwidth <- ppc_dots(vdiff_y, vdiff_yrep[1:8, ], binwidth = 3, quantiles = 50)
+ expect_warning(vdiffr::expect_doppelganger("ppc_dots (quantile-binwidth)", p_quantile_binwidth),
+ "The provided binwidth will cause dots to overflow the boundaries")
+
+ # ppd versions
+ p_base <- ppd_dots(vdiff_yrep[1:8, ])
+ vdiffr::expect_doppelganger("ppd_dots (default)", p_base)
+
+ p_binwidth <- ppd_dots(vdiff_yrep[1:8, ], binwidth = 3)
+ expect_warning(vdiffr::expect_doppelganger("ppd_dots (binwidth)", p_binwidth),
+ "The provided binwidth will cause dots to overflow the boundaries")
+
+ p_quantile <- ppd_dots(vdiff_yrep[1:8, ], quantiles = 50)
+ vdiffr::expect_doppelganger("ppd_dots (quantile)", p_quantile)
+
+ p_quantile_binwidth <- ppd_dots(vdiff_yrep[1:8, ], binwidth = 3, quantiles = 50)
+ expect_warning(vdiffr::expect_doppelganger("ppd_dots (quantile-binwidth)", p_quantile_binwidth),
+ "The provided binwidth will cause dots to overflow the boundaries")
+})
+
test_that("ppc_ecdf_overlay renders correctly", {
testthat::skip_on_cran()
testthat::skip_if_not_installed("vdiffr")