From 6b86e130d30be773f73d7527a1def46b84800c41 Mon Sep 17 00:00:00 2001 From: jgabry Date: Wed, 18 Apr 2018 14:42:59 -0400 Subject: [PATCH 1/3] Introduce facet_relabel_gg and facet_vars helpers Advances #75 --- NAMESPACE | 2 + R/bayesplot-helpers.R | 150 +++++++++++++++++--- man/bayesplot-helpers.Rd | 94 ++++++++++-- tests/testthat/test-convenience-functions.R | 40 ++++++ 4 files changed, 254 insertions(+), 32 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 178484bf..36592a6a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -30,7 +30,9 @@ export(example_x_data) export(example_y_data) export(example_yrep_draws) export(facet_bg) +export(facet_relabel_gg) export(facet_text) +export(facet_vars) export(grid_lines) export(hline_0) export(hline_at) diff --git a/R/bayesplot-helpers.R b/R/bayesplot-helpers.R index b535aa73..0843c465 100644 --- a/R/bayesplot-helpers.R +++ b/R/bayesplot-helpers.R @@ -2,7 +2,7 @@ #' #' Convenience functions for adding to (and changing details of) ggplot objects #' (many of the objects returned by \pkg{bayesplot} functions). See the -#' \strong{Examples} section, below. +#' \strong{Details} and \strong{Examples} sections, below. #' #' @name bayesplot-helpers #' @@ -23,12 +23,26 @@ #' For \code{overlay_function}, \code{...} is passed to #' \code{\link[ggplot2]{stat_function}}. #' +#' For \code{facet_relabel_gg}, \code{...} is passed to +#' \code{\link[ggplot2]{facet_wrap}} or \code{\link[ggplot2]{facet_grid}}. +#' #' @return -#' A \pkg{ggplot2} layer or \code{\link[ggplot2]{theme}} object that can be -#' added to existing ggplot objects, like those created by many of the -#' \pkg{bayesplot} plotting functions. See the \strong{Details} section. +#' Most of these functions return a \pkg{ggplot2} layer or +#' \code{\link[ggplot2]{theme}} object that can be added to existing ggplot +#' objects, like those created by many of the \pkg{bayesplot} plotting +#' functions. +#' +#' However, there are a few exceptions. Functions with names ending in +#' \code{_gg} (e.g., \code{facet_relabel_gg}) return an entire ggplot object (a +#' modified version of the ggplot object used as the input to the function). A +#' few other functions (e.g., \code{facet_vars}) return useful info about the +#' object passed in as input. See the \strong{Details} section for more +#' information. #' #' @details +#' These subsections provide more details on the individual helper functions. +#' See the \strong{Examples} section for usage demonstrations. +#' #' \subsection{Add vertical, horizontal, and diagonal lines to plots}{ #' \itemize{ #' \item \code{vline_at} and \code{hline_at} return an object created by either @@ -50,13 +64,33 @@ #' \code{med} is \code{TRUE}). #' } #' } -#' \subsection{Control appearance of facet strips}{ +#' +#' \subsection{Control appearance of facet strips and change their labels}{ #' \itemize{ #' \item \code{facet_text} and \code{facet_bg} return ggplot2 theme objects that #' can be added to an existing plot (ggplot object) to format the text and the #' background for the facet strips. +#' +#' \item \code{facet_relabel_gg} is atypical in that it accepts a ggplot object +#' as input (so it can detect the variable currently used for faceting) and +#' returns a modified version of the same ggplot object as its output (with +#' update facet labels). The \code{...} can be use with \code{facet_relabel_gg} +#' to manually pass other arguments to \code{facet_wrap} or \code{facet_grid} +#' other than \code{"facets"} and \code{"scales"} arguments, which are +#' automatically inferred from the plot object. +#' +#' \item \code{facet_vars} takes a ggplot object as input and returns a +#' character vector indicating the name of the variable(s) \pkg{bayesplot} uses +#' internally to create facets (if any). If \code{facet_relabel_gg} isn't +#' flexible enough to relabel the facets how you want then \code{facet_vars} +#' gives you the info you need to add \code{\link[ggplot2]{facet_wrap}} (or +#' \code{\link[ggplot2]{facet_grid}}) to the object yourself. The \code{"scales"} +#' attribute of the \code{facet_vars} result indicates the value \pkg{bayesplot} +#' used for the \code{scales} argument to \code{facet_wrap} (or +#' \code{facet_grid}). #' } #' } +#' #' \subsection{Move legend, remove legend, or style the legend text}{ #' \itemize{ #' \item \code{legend_move} and \code{legend_none} return a ggplot2 theme object @@ -66,6 +100,7 @@ #' except it controls the legend text. #' } #' } +#' #' \subsection{Control appearance of \eqn{x}-axis and \eqn{y}-axis features}{ #' \itemize{ #' \item \code{xaxis_title} and \code{yaxis_title} return a ggplot2 theme object @@ -83,6 +118,7 @@ #' appearance of the axis tick marks. #' } #' } +#' #' \subsection{Customize plot background}{ #' \itemize{ #' \item \code{plot_bg} returns a ggplot2 theme object that can be added to an @@ -94,6 +130,7 @@ #' an existing plot (ggplot object) to add grid lines to the plot background. #' } #' } +#' #' \subsection{Superimpose a function on an existing plot}{ #' \itemize{ #' \item \code{overlay_function} is a simple wrapper for @@ -158,9 +195,9 @@ #' size = 2, alpha = 0.75) #' #' -#' ########################## -#' ### format axis titles ### -#' ########################## +#' ################################### +#' ### format axis titles and text ### +#' ################################### #' color_scheme_set("green") #' y <- example_y_data() #' yrep <- example_yrep_draws() @@ -172,24 +209,39 @@ #' xaxis_title(size = 13, family = "sans") + #' ggplot2::xlab(expression(italic(T(y)) == median(italic(y)))) #' +#' # change the style of the xaxis text, for example make it +#' # bigger and the same color as the colors used in the plot +#' clrs <- color_scheme_get() # get current color scheme +#' p3 + xaxis_text(size = 20, color = clrs$mid) #' -#' ################################ -#' ### format axis & facet text ### -#' ################################ -#' color_scheme_set("gray") +#' ########################################### +#' ### format facet text and change labels ### +#' ########################################### +#' color_scheme_set("darkgray") #' p4 <- mcmc_trace(example_mcmc_draws(), pars = c("alpha", "sigma")) +#' plot(p4) #' #' myfacets <- #' facet_bg(fill = "gray30", color = NA) + #' facet_text(face = "bold", color = "skyblue", size = 14) -#' p4 + myfacets +#' (p4 <- p4 + myfacets) +#' +#' # also change facet labels +#' new_labs <- c("alpha" = "Owl", "sigma" = "Falcon") +#' (p4 <- facet_relabel_gg(p4, labels = new_labs)) +#' +#' # facet_vars returns info about the faceting +#' facet_vars(p4) +#' +#' # relabeling facets in grouped PPC plots +#' +#' #' #' \donttest{ -#' ########################## -#' ### control tick marks ### -#' ########################## +#' ############################## +#' ### format axis tick marks ### +#' ############################## #' p4 + -#' myfacets + #' yaxis_text(FALSE) + #' yaxis_ticks(FALSE) + #' xaxis_ticks(size = 1, color = "skyblue") @@ -424,6 +476,70 @@ facet_bg <- function(on = TRUE, ...) { element_blank()) } +#' @rdname bayesplot-helpers +#' @export +#' @param gg A ggplot object. +#' @param labels For facet relabelling, \code{labels} can be a named character +#' vector where the names are the current labels and the values are the new +#' labels to use, or \code{labels} can be a labeller function as described in +#' \code{\link[ggplot2]{facet_wrap}}. +#' +facet_relabel_gg <- function(gg, labels = NULL, ...) { + stopifnot(inherits(gg, "ggplot"), !is.null(labels)) + if (!inherits(labels, "labeller")) { + labels <- ggplot2::as_labeller(labels) + } + vars <- facet_vars(gg) + + if (length(vars) > 1) { + facets <- paste(vars[1], "~", vars[2]) + gg <- + gg + facet_grid(facets, + labeller = labels, + scales = attr(vars, "scales"), + ...) + } else { + facets <- vars[[1]] + gg <- + gg + facet_wrap(facets, + labeller = labels, + scales = attr(vars, "scales"), + ...) + } + + return(gg) +} + +#' @rdname bayesplot-helpers +#' @export +facet_vars <- function(gg) { + pars <- gg$facet$params + + if (!is.null(pars[["facets"]])) { + out <- c(facet_wrap = names(pars$facets)) + } else if (!is.null(pars[["rows"]]) && + !is.null(pars[["cols"]])) { + out <- c(facet_grid_rows = names(pars$rows), + facet_grid_cols = names(pars$cols)) + } else { + stop("Facets not found for this plot.") + } + + free <- pars[["free"]] + if (free[["x"]] && free[["y"]]) { + scales <- "free" + } else if (free[["x"]]) { + scales <- "free_x" + } else if (free[["y"]]) { + scales <- "free_y" + } else { + scales <- "fixed" + } + attr(out, "scales") <- scales + + return(out) +} + # plot background --------------------------------------------------------- #' @rdname bayesplot-helpers #' @export diff --git a/man/bayesplot-helpers.Rd b/man/bayesplot-helpers.Rd index c5f01b50..7cf88987 100644 --- a/man/bayesplot-helpers.Rd +++ b/man/bayesplot-helpers.Rd @@ -19,6 +19,8 @@ \alias{yaxis_ticks} \alias{facet_text} \alias{facet_bg} +\alias{facet_relabel_gg} +\alias{facet_vars} \alias{panel_bg} \alias{plot_bg} \alias{grid_lines} @@ -59,6 +61,10 @@ facet_text(on = TRUE, ...) facet_bg(on = TRUE, ...) +facet_relabel_gg(gg, labels = NULL, ...) + +facet_vars(gg) + panel_bg(on = TRUE, ...) plot_bg(on = TRUE, ...) @@ -90,7 +96,10 @@ vector.} \code{\link[ggplot2]{element_line}}. For \code{overlay_function}, \code{...} is passed to - \code{\link[ggplot2]{stat_function}}.} + \code{\link[ggplot2]{stat_function}}. + + For \code{facet_relabel_gg}, \code{...} is passed to + \code{\link[ggplot2]{facet_wrap}} or \code{\link[ggplot2]{facet_grid}}.} \item{na.rm}{A logical scalar passed to the appropriate geom (e.g. \code{\link[ggplot2]{geom_vline}}). The default is \code{TRUE}.} @@ -113,19 +122,37 @@ For example, facet text can be removed by adding object. If \code{on=TRUE} (the default), then \code{...} can be used to customize the appearance of the theme element.} +\item{gg}{A ggplot object.} + +\item{labels}{For facet relabelling, \code{labels} can be a named character +vector where the names are the current labels and the values are the new +labels to use, or \code{labels} can be a labeller function as described in +\code{\link[ggplot2]{facet_wrap}}.} + \item{color, size}{Passed to \code{\link[ggplot2]{element_line}}.} } \value{ -A \pkg{ggplot2} layer or \code{\link[ggplot2]{theme}} object that can be -added to existing ggplot objects, like those created by many of the -\pkg{bayesplot} plotting functions. See the \strong{Details} section. +Most of these functions return a \pkg{ggplot2} layer or +\code{\link[ggplot2]{theme}} object that can be added to existing ggplot +objects, like those created by many of the \pkg{bayesplot} plotting +functions. + +However, there are a few exceptions. Functions with names ending in +\code{_gg} (e.g., \code{facet_relabel_gg}) return an entire ggplot object (a +modified version of the ggplot object used as the input to the function). A +few other functions (e.g., \code{facet_vars}) return useful info about the +object passed in as input. See the \strong{Details} section for more +information. } \description{ Convenience functions for adding to (and changing details of) ggplot objects (many of the objects returned by \pkg{bayesplot} functions). See the -\strong{Examples} section, below. +\strong{Details} and \strong{Examples} sections, below. } \details{ +These subsections provide more details on the individual helper functions. +See the \strong{Examples} section for usage demonstrations. + \subsection{Add vertical, horizontal, and diagonal lines to plots}{ \itemize{ \item \code{vline_at} and \code{hline_at} return an object created by either @@ -147,13 +174,33 @@ set to 0 and the slope set to 1. \code{med} is \code{TRUE}). } } -\subsection{Control appearance of facet strips}{ + +\subsection{Control appearance of facet strips and change their labels}{ \itemize{ \item \code{facet_text} and \code{facet_bg} return ggplot2 theme objects that can be added to an existing plot (ggplot object) to format the text and the background for the facet strips. + +\item \code{facet_relabel_gg} is atypical in that it accepts a ggplot object +as input (so it can detect the variable currently used for faceting) and +returns a modified version of the same ggplot object as its output (with +update facet labels). The \code{...} can be use with \code{facet_relabel_gg} +to manually pass other arguments to \code{facet_wrap} or \code{facet_grid} +other than \code{"facets"} and \code{"scales"} arguments, which are +automatically inferred from the plot object. + +\item \code{facet_vars} takes a ggplot object as input and returns a +character vector indicating the name of the variable(s) \pkg{bayesplot} uses +internally to create facets (if any). If \code{facet_relabel_gg} isn't +flexible enough to relabel the facets how you want then \code{facet_vars} +gives you the info you need to add \code{\link[ggplot2]{facet_wrap}} (or +\code{\link[ggplot2]{facet_grid}}) to the object yourself. The \code{"scales"} +attribute of the \code{facet_vars} result indicates the value \pkg{bayesplot} +used for the \code{scales} argument to \code{facet_wrap} (or +\code{facet_grid}). } } + \subsection{Move legend, remove legend, or style the legend text}{ \itemize{ \item \code{legend_move} and \code{legend_none} return a ggplot2 theme object @@ -163,6 +210,7 @@ position of the legend (\code{legend_move}) or remove the legend except it controls the legend text. } } + \subsection{Control appearance of \eqn{x}-axis and \eqn{y}-axis features}{ \itemize{ \item \code{xaxis_title} and \code{yaxis_title} return a ggplot2 theme object @@ -180,6 +228,7 @@ that can be added to an existing plot (ggplot object) to change the appearance of the axis tick marks. } } + \subsection{Customize plot background}{ \itemize{ \item \code{plot_bg} returns a ggplot2 theme object that can be added to an @@ -191,6 +240,7 @@ plotting area. an existing plot (ggplot object) to add grid lines to the plot background. } } + \subsection{Superimpose a function on an existing plot}{ \itemize{ \item \code{overlay_function} is a simple wrapper for @@ -252,9 +302,9 @@ p2 + vline_at(b1, lbub(0.8, med = FALSE), color = "gray20", size = 2, alpha = 0.75) -########################## -### format axis titles ### -########################## +################################### +### format axis titles and text ### +################################### color_scheme_set("green") y <- example_y_data() yrep <- example_yrep_draws() @@ -266,24 +316,38 @@ p3 + xaxis_title(size = 13, family = "sans") + ggplot2::xlab(expression(italic(T(y)) == median(italic(y)))) +# change the style of the xaxis text, for example make it +# bigger and the same color as the colors used in the plot +clrs <- color_scheme_get() # get current color scheme +p3 + xaxis_text(size = 20, color = clrs$mid) -################################ -### format axis & facet text ### -################################ -color_scheme_set("gray") +########################################### +### format facet text and change labels ### +########################################### +color_scheme_set("darkgray") p4 <- mcmc_trace(example_mcmc_draws(), pars = c("alpha", "sigma")) +plot(p4) myfacets <- facet_bg(fill = "gray30", color = NA) + facet_text(face = "bold", color = "skyblue", size = 14) -p4 + myfacets +(p4 <- p4 + myfacets) + +# also change facet labels +new_labs <- c("alpha" = "Owl", "sigma" = "Falcon") +(p4 <- facet_relabel_gg(p4, labels = new_labs)) + +# facet_vars returns info about the faceting +facet_vars(p4) + +# relabeling facets in grouped PPC plots + \donttest{ ########################## ### control tick marks ### ########################## p4 + - myfacets + yaxis_text(FALSE) + yaxis_ticks(FALSE) + xaxis_ticks(size = 1, color = "skyblue") diff --git a/tests/testthat/test-convenience-functions.R b/tests/testthat/test-convenience-functions.R index 49ae410e..99ac201b 100644 --- a/tests/testthat/test-convenience-functions.R +++ b/tests/testthat/test-convenience-functions.R @@ -137,6 +137,46 @@ test_that("facet_text returns correct theme object", { ) }) + +# facet relabelling ------------------------------------------------------- +test_that("facet_vars returns faceting info", { + expect_error( + facet_vars(mcmc_hist(example_mcmc_draws(params = 1))), + "Facets not found for this plot" + ) + + p <- mcmc_trace(example_mcmc_draws()) + facet_info <- facet_vars(p) + expect_equal(facet_info[[1]], "Parameter") + expect_named(facet_info, "facet_wrap") + expect_equal(attr(facet_info, "scales"), "free") + + # change scales + p2 <- mcmc_trace(example_mcmc_draws(), facet_args = list(scales = "fixed")) + facet_info2 <- facet_vars(p2) + expect_equal(attr(facet_info2, "scales"), "fixed") + + # test with plot that uses facet_grid + p3 <- ppc_error_hist_grouped(example_y_data(), example_yrep_draws()[1:4, ], + group = example_group_data()) + facet_info3 <- facet_vars(p3) + expect_named(facet_info3, c("facet_grid_rows", "facet_grid_cols")) + expect_equal(facet_info3[[1]], "rep_id") + expect_equal(facet_info3[[2]], "group") + expect_equal(attr(facet_info3, "scales"), "free") +}) + +test_that("facet_relabel_gg doesn't error", { + x <- example_mcmc_draws(params = 3) + p <- mcmc_trace(x, facet_args = list(scales = "free_y")) + new_labs <- setNames(c("a", "b", "c"), parameter_names(x)) + p2 <- facet_relabel_gg(p, labels = new_labs) + + # is facet_vars output on new plot still correct? + expect_equal(facet_vars(p2)[[1]], "Parameter") + expect_equal(attr(facet_vars(p2), "scales"), "free_y") +}) + # axis titles ------------------------------------------------------------- test_that("xaxis_title returns correct theme object", { expect_identical(xaxis_title(FALSE), xlab(NULL)) From 96da3a71d55a23f1bfcabe1d968dddf67f5e9e30 Mon Sep 17 00:00:00 2001 From: Thomas Kluth Date: Mon, 27 Aug 2018 12:10:24 +0200 Subject: [PATCH 2/3] add grouped PPC example for facet_relabel_gg --- R/bayesplot-helpers.R | 2 ++ 1 file changed, 2 insertions(+) diff --git a/R/bayesplot-helpers.R b/R/bayesplot-helpers.R index 0843c465..63d48de4 100644 --- a/R/bayesplot-helpers.R +++ b/R/bayesplot-helpers.R @@ -234,6 +234,8 @@ #' facet_vars(p4) #' #' # relabeling facets in grouped PPC plots +#' pgroup <- ppc_scatter_avg_grouped(example_y_data(), example_yrep_draws(), example_group_data()) +#' pgroup <- facet_relabel_gg(pgroup, labels = c("GroupA" = "apples", "GroupB" = "oranges")) #' #' #' From 525284e752b4f428bbb247ef090a94bd82c158fe Mon Sep 17 00:00:00 2001 From: jgabry Date: Tue, 23 Oct 2018 14:54:30 -0400 Subject: [PATCH 3/3] update NEWS --- NEWS.md | 2 +- man/bayesplot-helpers.Rd | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index 0da051f3..6cdca202 100644 --- a/NEWS.md +++ b/NEWS.md @@ -41,7 +41,7 @@ [`?ppc_loo_pit_overlay()`](http://mc-stan.org/bayesplot/reference/PPC-loo.html) now work as expected. (#166, #167) - +* New convenience functions `facet_relabel_gg()` and `facet_vars` which provide info about and allow changing the labels used for faceted plots. (advances #75, @silberzwiebel) # bayesplot 1.6.0 diff --git a/man/bayesplot-helpers.Rd b/man/bayesplot-helpers.Rd index 7cf88987..ea933419 100644 --- a/man/bayesplot-helpers.Rd +++ b/man/bayesplot-helpers.Rd @@ -341,12 +341,15 @@ new_labs <- c("alpha" = "Owl", "sigma" = "Falcon") facet_vars(p4) # relabeling facets in grouped PPC plots +pgroup <- ppc_scatter_avg_grouped(example_y_data(), example_yrep_draws(), example_group_data()) +pgroup <- facet_relabel_gg(pgroup, labels = c("GroupA" = "apples", "GroupB" = "oranges")) + \donttest{ -########################## -### control tick marks ### -########################## +############################## +### format axis tick marks ### +############################## p4 + yaxis_text(FALSE) + yaxis_ticks(FALSE) +