4

I have a function stacked_plot() that uses tidy evaluation to make a stacked plot. I wanted to include it in my package and make the other function from that package to call it. Here's the minimal example:

stacked_plot <- function(data, what, by = NULL, date_col = date){

  by <- rlang::enquo(by)
  what <- rlang::ensym(what)
  date_col <- rlang::ensym(date_col)
  data <- data %>%
    dplyr::group_by(!!date_col, !!by) %>%
    dplyr::summarise(!!what := sum(!!what, na.rm = TRUE)) %>%
    dplyr::ungroup() %>%
    tidyr::complete(!!date_col, !!by, fill = rlang::list2(!!what := 0))

  p <- data %>%
    ggplot2::ggplot(ggplot2::aes(!!date_col, !!what, fill = !!by)) +
    ggplot2::geom_area(position = 'stack')
  print(p)
}

#' @importFrom rlang .data
call_plot <- function() {
  to_plot <- data.frame(date = rep(seq(lubridate::ymd('2020-01-01'),
                                       lubridate::ymd('2020-03-30'),
                                       by = '1 day'), each = 3)) %>%
    dplyr::mutate(cat = rep(c('A', 'B', 'C'), 90), v1 = runif(270))

  p <- to_plot %>%
    stacked_plot(what = .data$v1, by = .data$cat)
  return(p)
}

Incorporating stacked_plot() into a package worked fine and I can call it interactively. However, the call_plot() after load_all() results in an error. Here's the rlang::last_error() output:

rlang::last_error()
<error/rlang_error>
Only strings can be converted to symbols
Backtrace:
  1. global::call_plot()
 10. mmmtools::stacked_plot(., what = .data$v1, by = .data$cat)
 11. rlang::ensym(what) R/stacked_plot.R:4:2
Run `rlang::last_trace()` to see the full context.

I believe this is due to some idiosyncracy of tidy evaluation. However I can't really find any source about pecularities of using tidyeval in R packages. The only thing I know is that I can't really use quoted variables directly and use .data$variable pattern instead, which I did in call_plot().

How can I use adjust stacked_plot() to make it usable from other package functions?

NelsonGon
  • 13,015
  • 7
  • 27
  • 57
Kuba_
  • 886
  • 6
  • 22
  • @akrun has given a good answer, but actually your set-up would work if you tried just calling `stacked_plot(what = v1, by = cat)` in your `call_plot` function. – Allan Cameron May 17 '20 at 00:46

1 Answers1

5

It seems that the OP wanted to take unquoted column names. If that is the case, change the ensym to enquo

stacked_plot <- function(data, what, by = NULL, date_col = date){

  by <- rlang::enquo(by)
  what <- rlang::enquo(what)
  date_col <- rlang::enquo(date_col)
  data %>%
    dplyr::group_by(!! date_col, !!by) %>%
    dplyr::summarise(!!what := sum(!!what, na.rm = TRUE)) %>%
      dplyr::ungroup() %>%
      tidyr::complete(!!date_col, !!by, fill = rlang::list2(!!what := 0)) %>%  
    ggplot2::ggplot(ggplot2::aes(!!date_col, !!what, fill = !!by)) +
     ggplot2::geom_area(position = 'stack')



}

call_plot <- function() {
  to_plot <- data.frame(date = rep(seq(lubridate::ymd('2020-01-01'),
                                       lubridate::ymd('2020-03-30'),
                                       by = '1 day'), each = 3)) %>%
    dplyr::mutate(cat = rep(c('A', 'B', 'C'), 90), v1 = runif(270))

  p <- to_plot %>%
    stacked_plot(what =  v1, by =  cat)
  return(p)
}

-call the function

call_plot()

enter image description here


In addition, the enquo + !! can be totally replaced with {{}}

stacked_plot <- function(data, what, by = NULL, date_col = date){



  data %>%
    dplyr::group_by({{date_col}}, {{by}}) %>%
    dplyr::summarise( {{what}} := sum({{what}}, na.rm = TRUE)) %>%
    dplyr::ungroup() %>%
    tidyr::complete({{date_col}}, {{by}}, fill = rlang::list2({{what}} := 0))  %>% 
    ggplot2::ggplot(ggplot2::aes({{date_col}}, {{what}}, fill = {{by}})) +
    ggplot2::geom_area(position = 'stack')



}

and it can be as in the previous one

call_plot()
akrun
  • 874,273
  • 37
  • 540
  • 662
  • As pointed out by @AllanCameron, `ensym` actually works just fine and the solution is to call `stacked_plot(what = v1, by = cat)` instead of `stacked_plot(what = .data$v1, by = .data$cat)`. However, it raises `no visible binding for global variable 'v1'` warning while package building and I wondered what's the good practice is as the recommended one with calling `.data$v1` apparently fails here. – Kuba_ Jun 24 '20 at 12:48
  • @kuba_ Can you please post as a new question. thanks – akrun Jun 24 '20 at 17:40
  • I know it's been a while since I opened this topic, but it's really what the initial question was about. Therefore, I'm not sure how it would benefit from opening new thread as the question and the example would be all the same. The only thing it changed now is that we know how to make it work, but it's still seems like a dirty solution as it fails pkgbuild checks. – Kuba_ Jun 24 '20 at 17:53