class: center, middle, title-slide # Best practices for programming with ggplot2 ##
### Dewey Dunnington (
@paleolimbot
) --- class: center, middle, inverse # It all started with a reverse dependency check... --- class: center, middle  (vignette at [ggplot2.tidyverse.org/dev](https://ggplot2.tidyverse.org/dev/articles/ggplot2-in-packages.html)) --- ```r library(ggplot2) ggplot(mpg) + geom_point(aes(displ, hwy, colour = class)) + facet_wrap(vars(drv)) ``` <!-- --> --- # Programming with ggplot2 ```r colour_var <- "class" facet_var <- "drv" ``` --- # Programming with ggplot2 ```r colour_var <- "class" facet_var <- "drv" ``` Mappings and facet specifications use tidy evaluation: ```r ggplot(mpg) + geom_point(aes(displ, hwy, colour = class)) + facet_wrap(vars(drv)) ``` <!-- --> --- # Programming with ggplot2 ```r colour_var <- "class" facet_var <- "drv" ``` Mappings and facet specifications use tidy evaluation: ```r ggplot(mpg) + geom_point(aes(displ, hwy, colour = .data[[colour_var]])) + facet_wrap(vars(.data[[facet_var]])) ``` <!-- --> --- # Programming with ggplot2 .hidden[ ```r colour_var <- "class" ``` Mappings and facet specifications use tidy evaluation: ] ```r plot_mpg <- function(colour_var, facet_var) { ggplot(mpg) + geom_point(aes(displ, hwy, colour = .data[[colour_var]])) + facet_wrap(vars(.data[[facet_var]])) } ``` --- # Programming with ggplot2 ```r plot_mpg("class", "drv") ``` <!-- --> --- # Programming with ggplot2 ```r plot_mpg(NULL, "drv") ``` ``` ## Error: Must subset the data pronoun with a string ``` <!-- --> --- # Programming with ggplot2 ```r plot_mpg <- function(colour_var, facet_var) { ggplot(mpg) + geom_point(aes(displ, hwy, colour = .data[[colour_var]])) + facet_wrap(vars(.data[[facet_var]])) } plot_mpg(NULL, "drv") ``` ``` ## Error: Must subset the data pronoun with a string ``` <!-- --> --- # Programming with ggplot2 ```r plot_mpg <- function(colour_var, facet_var) { mapping <- aes(displ, hwy, colour = .data[[colour_var]]) ggplot(mpg) + geom_point(mapping) + facet_wrap(vars(.data[[facet_var]])) } plot_mpg(NULL, "drv") ``` ``` ## Error: Must subset the data pronoun with a string ``` <!-- --> --- # Programming with ggplot2 ```r plot_mpg <- function(colour_var, facet_var) { mapping <- aes(displ, hwy, colour = .data[[colour_var]]) if (is.null(colour_var)) { mapping$colour <- NULL } ggplot(mpg) + geom_point(mapping) + facet_wrap(vars(.data[[facet_var]])) } plot_mpg(NULL, "drv") ``` <!-- --> --- # Programming with ggplot2 ```r plot_mpg <- function(colour_var, facet_var) { mapping <- aes(displ, hwy, colour = .data[[colour_var]]) if (is.null(colour_var)) { mapping$colour <- NULL } ggplot(mpg) + geom_point(mapping) + facet_wrap(vars(.data[[facet_var]])) } plot_mpg(NULL, NULL) ``` ``` ## Error: Must subset the data pronoun with a string ``` <!-- --> --- # Programming with ggplot2 ```r plot_mpg <- function(colour_var, facet_var) { mapping <- aes(displ, hwy, colour = .data[[colour_var]]) if (is.null(colour_var)) { mapping$colour <- NULL } facet <- facet_wrap(vars(.data[[facet_var]])) ggplot(mpg) + geom_point(mapping) + facet } plot_mpg(NULL, NULL) ``` ``` ## Error: Must subset the data pronoun with a string ``` <!-- --> --- # Programming with ggplot2 ```r plot_mpg <- function(colour_var, facet_var) { mapping <- aes(displ, hwy, colour = .data[[colour_var]]) if (is.null(colour_var)) { mapping$colour <- NULL } if (is.null(facet_var)) { facet <- NULL } else { facet <- facet_wrap(vars(.data[[facet_var]])) } ggplot(mpg) + geom_point(mapping) + facet } ``` --- ```r plot_mpg(NULL, NULL) ``` <!-- --> --- ```r plot_mpg("class", NULL) ``` <!-- --> --- class: center, middle, inverse # Use <br/><br/> `.data[["variable_name"]]` <br/><br/> within <br/><br/> `aes()` and/or `vars()` --- class: center, middle, inverse # `ggplot(...) + NULL` <br/><br/> is identical to <br/><br/> `ggplot(...)` <br/><br/> Use it to conditinally add an item to a plot! --- class: center, middle, inverse # `ggplot(...) + item1 + item2` <br/><br/> is identical to <br/><br/> `ggplot(...) + list(item1, item2)` <br/><br/> Use it to conditinally more than one item to a plot! --- # Introducing...the {plotmpg} package! ```r library(plotmpg) plot_mpg("class", "drv") ``` <!-- --> --- class: center, middle, inverse # Does your package need a plot function? # (consider documentation instead) --- # Using ggplot2 in packages: namespacing ```r #' #' plot_mpg <- function(colour_var, facet_var) { mapping <- aes(displ, hwy, colour = .data[[colour_var]]) if (is.null(colour_var)) { mapping$colour <- NULL } if (is.null(facet_var)) { facet <- NULL } else { facet <- facet_wrap(vars(.data[[facet_var]])) } ggplot(mpg) + geom_point(mapping) + facet } ``` --- # Using ggplot2 in packages: namespacing ```r #' @importFrom ggplot2 ggplot aes vars facet_wrap geom_point labs #' plot_mpg <- function(colour_var, facet_var) { mapping <- aes(displ, hwy, colour = .data[[colour_var]]) if (is.null(colour_var)) { mapping$colour <- NULL } if (is.null(facet_var)) { facet <- NULL } else { facet <- facet_wrap(vars(.data[[facet_var]])) } ggplot(ggplot2::mpg) + geom_point(mapping) + facet } ``` --- # Using ggplot2 in packages: undefined variables ``` ── R CMD check results ──────────── plotmpg 0.0.0.9000 ──── Duration: 25.2s > checking R code for possible problems ... NOTE plot_mpg: no visible binding for global variable ‘.data’ plot_mpg: no visible binding for global variable ‘displ’ plot_mpg: no visible binding for global variable ‘hwy’ Undefined global functions or variables: .data displ hwy 0 errors ✓ | 0 warnings ✓ | 1 note x ``` --- # Using ggplot2 in packages: undefined variables ```r #' @importFrom ggplot2 ggplot aes vars facet_wrap geom_point labs #' plot_mpg <- function(colour_var, facet_var) { mapping <- aes(displ, hwy, colour = .data[[colour_var]]) if (is.null(colour_var)) { mapping$colour <- NULL } if (is.null(facet_var)) { facet <- NULL } else { facet <- facet_wrap(vars(.data[[facet_var]])) } ggplot(ggplot2::mpg) + geom_point(mapping) + facet } ``` --- # Using ggplot2 in packages: undefined variables ```r #' @importFrom ggplot2 ggplot aes vars facet_wrap geom_point labs #' @importFrom rlang .data plot_mpg <- function(colour_var, facet_var) { mapping <- aes(.data$displ, .data$hwy, colour = .data[[colour_var]]) if (is.null(colour_var)) { mapping$colour <- NULL } if (is.null(facet_var)) { facet <- NULL } else { facet <- facet_wrap(vars(.data[[facet_var]])) } ggplot(ggplot2::mpg) + geom_point(mapping) + facet } ``` --- # Using ggplot2 in packages: undefined variables ``` ── R CMD check results ──────────── plotmpg 0.0.0.9000 ──── Duration: 26.8s 0 errors ✓ | 0 warnings ✓ | 0 notes ✓ ``` --- # Using ggplot2 in packages: testing Set up testing, install [vdiffr](http://vdiffr.r-lib.org/): ``` r # install.packages("vdiffr") usethis::use_test("plot-mpg") usethis::use_package("vdiffr", "Suggests") ``` ``` r # currently, vdiffr requires a context at the top of each file context("test-plot-mpg") test_that("plot_mpg() works", { vdiffr::expect_doppelganger( "plot_mpg(), defaults", plot_mpg() ) }) ``` --- # Using ggplot2 in packages: testing ``` r vdiffr::manage_cases() ## Running testthat to collect visual cases ## ## N = New visual case ## X = Failed doppelganger ## o = Successful doppelganger ## ## N ## Loading required package: shiny ## ## Listening on http://127.0.0.1:4164 ``` --- # Using ggplot2 in packages: testing  --- class: center, middle, inverse # Namespace using <br/><br/> `ggplot2::function()` <br/><br/> and/or <br/><br/> `#' @importFrom ggplot2 function` --- class: center, middle, inverse # Avoid check problems by using <br/><br/> `.data$var` <br/><br/> within `aes()` and `vars()` --- class: center, middle, inverse # Test using {vdiffr}! --- ```r library(electionca) # https://paleolimbot.github.io/electionca plot_votes(1867:2019, position = "fill") + guides( x = guide_axis(check.overlap = TRUE), fill = guide_legend(ncol = 2) ) ``` <!-- --> --- class: center, middle # Best practices for programming with ggplot2 <img src="images/ggplot2.png" style="width: 200px" /> Slides: [fishandwhistle.net/slides/rstudioconf2020](https://dewey.dunnington.ca/slides/rstudioconf2020) Code: [github.com/paleolimbot/rstudioconf2020](https://github.com/paleolimbot/rstudioconf2020) Vignette: [Using ggplot2 in packages](https://ggplot2.tidyverse.org/dev/articles/ggplot2-in-packages.html) @paleolimbot on [Twitter](https://twitter.com/paleolimbot) and [GitHub](https://github.com/paleolimbot)