diff --git a/NAMESPACE b/NAMESPACE
index 4823d8df..b8cf818b 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -74,6 +74,8 @@ export(mcmc_dens)
export(mcmc_dens_chains)
export(mcmc_dens_chains_data)
export(mcmc_dens_overlay)
+export(mcmc_dots)
+export(mcmc_dots_by_chain)
export(mcmc_hex)
export(mcmc_hist)
export(mcmc_hist_by_chain)
diff --git a/NEWS.md b/NEWS.md
index a8160721..5f90aa54 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,5 +1,7 @@
# bayesplot (development version)
+* New functions `mcmc_dots` and `mcmc_dots_by_chain` for dot plots of MCMC draws by @behramulukir (#402)
+
# bayesplot 1.15.0
* Add `shape` argument to `mcmc_scatter` by @behramulukir (#375)
diff --git a/R/mcmc-distributions.R b/R/mcmc-distributions.R
index 0b7acc0e..767d66c9 100644
--- a/R/mcmc-distributions.R
+++ b/R/mcmc-distributions.R
@@ -12,7 +12,7 @@
#' @template args-transformations
#' @template args-facet_args
#' @template args-density-controls
-#' @param ... Currently ignored.
+#' @param ... For dot plots, optional additional arguments to pass to [ggdist::stat_dots()].
#' @param alpha Passed to the geom to control the transparency.
#'
#' @template return-ggplot
@@ -25,6 +25,9 @@
#' \item{`mcmc_dens()`}{
#' Kernel density plots of posterior draws with all chains merged.
#' }
+#' \item{`mcmc_dots()`}{
+#' Dot plots of posterior draws with all chains merged.
+#' }
#' \item{`mcmc_hist_by_chain()`}{
#' Histograms of posterior draws with chains separated via faceting.
#' }
@@ -32,6 +35,9 @@
#' Kernel density plots of posterior draws with chains separated but
#' overlaid on a single plot.
#' }
+#' \item{`mcmc_dots_by_chain()`}{
+#' Dot plots of posterior draws with chains separated via faceting.
+#' }
#' \item{`mcmc_violin()`}{
#' The density estimate of each chain is plotted as a violin with
#' horizontal lines at notable quantiles.
@@ -100,6 +106,33 @@
#' color_scheme_set("green")
#' mcmc_violin(x) + panel_bg(color = "gray20", size = 2, fill = "gray30")
#'
+#'
+#' #################
+#' ### Dot Plots ###
+#' #################
+#'
+#' # dot plots of all parameters
+#' \donttest{
+#' mcmc_dots(x)
+#' }
+#'
+#' # dot plots of some parameters
+#' \donttest{
+#' color_scheme_set("pink")
+#' mcmc_dots(x, pars = c("alpha", "beta[2]"))
+#' }
+#'
+#' # example of using 'transformations' argument to plot log(sigma),
+#' # and and using 'quantiles' to plot number of dots corresponding to quantiles
+#' \donttest{
+#' mcmc_dots(x, transformations = list(sigma = "log"), quantiles = 80)
+#' }
+#'
+#' # separate dot plots by chain
+#' \donttest{
+#' mcmc_dots_by_chain(x, regex_pars = "beta")
+#' }
+#'
NULL
#' @rdname MCMC-distributions
@@ -362,7 +395,67 @@ mcmc_violin <- function(
)
}
+#' @rdname MCMC-distributions
+#' @export
+#' @template args-dots
+mcmc_dots <- function(
+ x,
+ pars = character(),
+ regex_pars = character(),
+ transformations = list(),
+ ...,
+ facet_args = list(),
+ binwidth = NA,
+ alpha = 1,
+ quantiles = NA
+) {
+ check_ignored_arguments(..., ok_args = c("dotsize", "layout", "stackratio", "overflow"))
+ suggested_package("ggdist")
+
+ .mcmc_dots(
+ x,
+ pars = pars,
+ regex_pars = regex_pars,
+ transformations = transformations,
+ binwidth = binwidth,
+ facet_args = facet_args,
+ alpha = alpha,
+ quantiles = quantiles,
+ ...
+ )
+}
+
+#' @rdname MCMC-distributions
+#' @export
+mcmc_dots_by_chain <- function(
+ x,
+ pars = character(),
+ regex_pars = character(),
+ transformations = list(),
+ ...,
+ facet_args = list(),
+ binwidth = NA,
+ alpha = 1,
+ quantiles = NA
+) {
+ check_ignored_arguments(..., ok_args = c("dotsize", "layout", "stackratio", "overflow"))
+
+ suggested_package("ggdist")
+
+ .mcmc_dots(
+ x,
+ pars = pars,
+ regex_pars = regex_pars,
+ transformations = transformations,
+ binwidth = binwidth,
+ facet_args = facet_args,
+ by_chain = TRUE,
+ alpha = alpha,
+ quantiles = quantiles,
+ ...
+ )
+}
# internal -----------------------------------------------------------------
@@ -539,3 +632,64 @@ mcmc_violin <- function(
yaxis_title(on = n_param == 1 && violin) +
xaxis_title(on = n_param == 1)
}
+
+
+.mcmc_dots <- function(
+ x,
+ pars = character(),
+ regex_pars = character(),
+ transformations = list(),
+ facet_args = list(),
+ binwidth = NA,
+ by_chain = FALSE,
+ alpha = 1,
+ quantiles = NA,
+ ...
+) {
+ x <- prepare_mcmc_array(x, pars, regex_pars, transformations)
+
+ if (by_chain && !has_multiple_chains(x)) {
+ STOP_need_multiple_chains()
+ }
+
+ data <- melt_mcmc(x, value.name = "value")
+ n_param <- num_params(data)
+
+ graph <- ggplot(data, aes(x = .data$value)) +
+ ggdist::stat_dots(
+ binwidth = binwidth,
+ quantiles = quantiles,
+ fill = get_color("mid"),
+ color = get_color("mid_highlight"),
+ alpha = alpha,
+ ...
+ )
+
+ facet_args[["scales"]] <- facet_args[["scales"]] %||% "free"
+ if (!by_chain) {
+ if (n_param > 1) {
+ facet_args[["facets"]] <- vars(.data$Parameter)
+ graph <- graph + do.call("facet_wrap", facet_args)
+ }
+ } else {
+ facet_args[["rows"]] <- vars(.data$Chain)
+ if (n_param > 1) {
+ facet_args[["cols"]] <- vars(.data$Parameter)
+ }
+ graph <- graph +
+ do.call("facet_grid", facet_args) +
+ force_axes_in_facets()
+ }
+
+ if (n_param == 1) {
+ graph <- graph + xlab(levels(data$Parameter))
+ }
+
+ graph +
+ dont_expand_y_axis(c(0.005, 0)) +
+ bayesplot_theme_get() +
+ yaxis_text(FALSE) +
+ yaxis_title(FALSE) +
+ yaxis_ticks(FALSE) +
+ xaxis_title(on = n_param == 1)
+}
diff --git a/man/MCMC-distributions.Rd b/man/MCMC-distributions.Rd
index deff7e48..4ef4cdb0 100644
--- a/man/MCMC-distributions.Rd
+++ b/man/MCMC-distributions.Rd
@@ -9,6 +9,8 @@
\alias{mcmc_dens_chains}
\alias{mcmc_dens_chains_data}
\alias{mcmc_violin}
+\alias{mcmc_dots}
+\alias{mcmc_dots_by_chain}
\title{Histograms and kernel density plots of MCMC draws}
\usage{
mcmc_hist(
@@ -102,6 +104,30 @@ mcmc_violin(
facet_args = list(),
probs = c(0.1, 0.5, 0.9)
)
+
+mcmc_dots(
+ x,
+ pars = character(),
+ regex_pars = character(),
+ transformations = list(),
+ ...,
+ facet_args = list(),
+ binwidth = NA,
+ alpha = 1,
+ quantiles = NA
+)
+
+mcmc_dots_by_chain(
+ x,
+ pars = character(),
+ regex_pars = character(),
+ transformations = list(),
+ ...,
+ facet_args = list(),
+ binwidth = NA,
+ alpha = 1,
+ quantiles = NA
+)
}
\arguments{
\item{x}{An object containing MCMC draws:
@@ -150,7 +176,7 @@ parameter is transformed (e.g. \code{"t(sigma)"}).
Note: due to partial argument matching \code{transformations} can be
abbreviated for convenience in interactive use (e.g., \code{transform}).}
-\item{...}{Currently ignored.}
+\item{...}{For dot plots, optional additional arguments to pass to \code{\link[ggdist:stat_dots]{ggdist::stat_dots()}}.}
\item{facet_args}{A named list of arguments (other than \code{facets}) passed
to \code{\link[ggplot2:facet_wrap]{ggplot2::facet_wrap()}} or \code{\link[ggplot2:facet_grid]{ggplot2::facet_grid()}}
@@ -186,6 +212,11 @@ parameters. \code{n_dens} defaults to \code{1024}.}
\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.}
+
+\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.}
}
\value{
A ggplot object that can be further customized using the \strong{ggplot2} package.
@@ -203,6 +234,9 @@ Histograms of posterior draws with all chains merged.
\item{\code{mcmc_dens()}}{
Kernel density plots of posterior draws with all chains merged.
}
+\item{\code{mcmc_dots()}}{
+Dot plots of posterior draws with all chains merged.
+}
\item{\code{mcmc_hist_by_chain()}}{
Histograms of posterior draws with chains separated via faceting.
}
@@ -210,6 +244,9 @@ Histograms of posterior draws with chains separated via faceting.
Kernel density plots of posterior draws with chains separated but
overlaid on a single plot.
}
+\item{\code{mcmc_dots_by_chain()}}{
+Dot plots of posterior draws with chains separated via faceting.
+}
\item{\code{mcmc_violin()}}{
The density estimate of each chain is plotted as a violin with
horizontal lines at notable quantiles.
@@ -279,6 +316,33 @@ mcmc_dens_chains(x2, pars = c("beta[1]", "beta[2]", "beta[3]"))
color_scheme_set("green")
mcmc_violin(x) + panel_bg(color = "gray20", size = 2, fill = "gray30")
+
+#################
+### Dot Plots ###
+#################
+
+# dot plots of all parameters
+\donttest{
+mcmc_dots(x)
+}
+
+# dot plots of some parameters
+\donttest{
+color_scheme_set("pink")
+mcmc_dots(x, pars = c("alpha", "beta[2]"))
+}
+
+# example of using 'transformations' argument to plot log(sigma),
+# and and using 'quantiles' to plot number of dots corresponding to quantiles
+\donttest{
+mcmc_dots(x, transformations = list(sigma = "log"), quantiles = 80)
+}
+
+# separate dot plots by chain
+\donttest{
+mcmc_dots_by_chain(x, regex_pars = "beta")
+}
+
}
\seealso{
Other MCMC:
diff --git a/tests/testthat/_snaps/mcmc-distributions/mcmc-dots-by-chain-default.svg b/tests/testthat/_snaps/mcmc-distributions/mcmc-dots-by-chain-default.svg
new file mode 100644
index 00000000..356c5b9e
--- /dev/null
+++ b/tests/testthat/_snaps/mcmc-distributions/mcmc-dots-by-chain-default.svg
@@ -0,0 +1,4193 @@
+
+
diff --git a/tests/testthat/_snaps/mcmc-distributions/mcmc-dots-default.svg b/tests/testthat/_snaps/mcmc-distributions/mcmc-dots-default.svg
new file mode 100644
index 00000000..863fa878
--- /dev/null
+++ b/tests/testthat/_snaps/mcmc-distributions/mcmc-dots-default.svg
@@ -0,0 +1,678 @@
+
+
diff --git a/tests/testthat/test-mcmc-distributions.R b/tests/testthat/test-mcmc-distributions.R
index 5a6f670a..00d31387 100644
--- a/tests/testthat/test-mcmc-distributions.R
+++ b/tests/testthat/test-mcmc-distributions.R
@@ -41,6 +41,21 @@ test_that("mcmc_dens returns a ggplot object", {
expect_gg(mcmc_dens(dframe1))
})
+test_that("mcmc_dots returns a ggplot object", {
+ expect_gg(mcmc_dots(arr, pars = "beta[2]", regex_pars = "x\\:"))
+ expect_gg(mcmc_dots(arr1chain, regex_pars = "beta"))
+ expect_gg(mcmc_dots(drawsarr, pars = "theta[1]", quantiles = 100))
+ expect_gg(mcmc_dots(drawsarr1chain, regex_pars = "theta"))
+ expect_gg(mcmc_dots(mat))
+ expect_gg(mcmc_dots(dframe))
+ expect_gg(mcmc_dots(dframe_multiple_chains))
+
+ expect_gg(mcmc_dots(arr1))
+ expect_gg(mcmc_dots(drawsarr1, quantiles = 67))
+ expect_gg(mcmc_dots(mat1))
+ expect_gg(mcmc_dots(dframe1))
+})
+
# functions that require multiple chains ----------------------------------
test_that("mcmc_hist_by_chain returns a ggplot object", {
@@ -86,6 +101,14 @@ test_that("mcmc_dens_chains/mcmc_dens_overlay color chains", {
expect_equal(get_palette(p4, 4), chain_colors(4))
})
+test_that("mcmc_dots_by_chain returns a ggplot object", {
+ expect_gg(mcmc_dots_by_chain(arr, pars = "beta[2]", regex_pars = "x\\:"))
+ expect_gg(mcmc_dots_by_chain(dframe_multiple_chains,
+ regex_pars = c("\\(Intercept\\)$", "beta")))
+ expect_gg(mcmc_dots_by_chain(arr, pars = "beta[2]", regex_pars = "x\\:",
+ quantiles = 80))
+})
+
test_that("mcmc_violin returns a ggplot object", {
expect_gg(mcmc_violin(arr, pars = "beta[2]", regex_pars = "x\\:"))
expect_gg(mcmc_violin(dframe_multiple_chains,
@@ -112,6 +135,11 @@ test_that("mcmc_* throws error if 1 chain but multiple chains required", {
expect_error(mcmc_violin(dframe), "requires multiple chains")
expect_error(mcmc_violin(arr1chain), "requires multiple chains")
expect_error(mcmc_violin(drawsarr1chain), "requires multiple chains")
+
+ expect_error(mcmc_dots_by_chain(mat), "requires multiple chains")
+ expect_error(mcmc_dots_by_chain(dframe), "requires multiple chains")
+ expect_error(mcmc_dots_by_chain(arr1chain), "requires multiple chains")
+ expect_error(mcmc_dots_by_chain(drawsarr1chain), "requires multiple chains")
})
@@ -181,3 +209,21 @@ test_that("mcmc_violin renders correctly", {
vdiffr::expect_doppelganger("mcmc_violin (default)", p_base)
})
+test_that("mcmc_dots renders correctly", {
+ testthat::skip_on_cran()
+ testthat::skip_if_not_installed("vdiffr")
+ skip_on_r_oldrel()
+
+ p_base <- mcmc_dots(vdiff_dframe)
+ vdiffr::expect_doppelganger("mcmc_dots (default)", p_base)
+})
+
+test_that("mcmc_dots_by_chain renders correctly", {
+ testthat::skip_on_cran()
+ testthat::skip_if_not_installed("vdiffr")
+ skip_on_r_oldrel()
+
+ p_base <- mcmc_dots_by_chain(vdiff_dframe_chains)
+ vdiffr::expect_doppelganger("mcmc_dots_by_chain (default)", p_base)
+})
+