How to use quosures within scoped variants after deprecation of funs()

This is related to the question I had here: Getting 'argument “e2” is missing with no default' error when using tidyeval within dplyr::summarize_at

While the error response is now more helpful, it's still not yet clear to me how to use quosures within scoped variants of mutate() and summarize().

Let's say I have the following data set:

  library(tidyverse)
  
  data <- tibble(
    weighting = rnorm(10, 1, 0.1),
    vara = rbinom(10, 1, 0.5),
    varb = rbinom(10, 1, 0.5),
    varc = rbinom(10, 1, 0.5)
  )

And I have this function that I want to apply to it:

myfun <- function(df, weight) {
    weight <- enquo(weight)
    summarize_at(df, vars(starts_with("var")), funs(sum(if_else(. == 1, !! weight, 0)) / sum(!! weight)))
  }

Before the full deprecation of funs() this would work fine:

myfun(data, weighting)

# A tibble: 1 x 3 
  vara  varb   varc 
  <dbl> <dbl> <dbl> 
  0.710 0.284 0.523

However, how would I do this in the future? My best attempt is the following, but it returns an error:

myfun2 <- function(df, weight) {
    weight <- enquo(weight)
    summarize_at(df, vars(starts_with("var")), list(~sum(if_else(. == 1, !! weight, 0)) / sum(!! weight))) 
  }

myfun2(data, weighting)
 Error: Quosures can only be unquoted within a quasiquotation context.

  # Bad:
  list(!!myquosure)

  # Good:
  dplyr::mutate(data, !!myquosure)

The error response tells me simply to not use the scoped variants, which is unhelpful to what I'm trying to do.

2 Likes

If you wanted to give your argument as a string you could take advantage of the .data pronoun as a work-around to quosures. (Differences in output below has to do with the seed; I used set.seed(16).)

myfun2 <- function(df, weight) {
     summarize_at(df, vars(starts_with("var")), 
                  list(~sum(if_else(. == 1, .data[[weight]], 0))/sum(.data[[weight]]) ) )
}

myfun2(data, "weighting")

# A tibble: 1 x 3
   vara  varb  varc
  <dbl> <dbl> <dbl>
1 0.701 0.609 0.287

I had a hard time turning the symbol into a string with tidyeval functions, which is a problem that looks similar to this question. This is a case where the old deparse()-substitute() approach could be used, but there may be something added to rlang since that post.

myfun3 <- function(df, weight) {
     weight = deparse(substitute(weight))
     summarize_at(df, vars(starts_with("var")), 
                  list(~sum(if_else(. == 1, .data[[weight]], 0))/sum(.data[[weight]])) )
}

myfun3(data, weighting)
# A tibble: 1 x 3
   vara  varb  varc
  <dbl> <dbl> <dbl>
1 0.701 0.609 0.287

Back to add that I really thought rlang::as_label() , er, rlang::as_name() should help for making things strings, but I couldn't figure it out yesterday. :stuck_out_tongue_winking_eye: Turns out it can be used after enquo() to do so, which I was missing.

myfun3 <- function(df, weight) {
     weight = rlang::as_name(enquo(weight))
     summarize_at(df, vars(starts_with("var")), 
                  list(~sum(if_else(. == 1, .data[[weight]], 0))/sum(.data[[weight]])) )
}
3 Likes

Thanks @aosmith, I appreciate the suggestions. I can't help but feel though that both possible solutions are "untidy" compared to the old funs() way... :frowning:

As you have noticed, as_label() works on any R object. If you want to get a column name (which is the case here since you're using the name to subset .data), it is safer to use as_name().

3 Likes

I was checking out the Github issue about this, which was recently closed. After updating dplyr to the current development version I could run your original myfun2 without error.

myfun2 <- function(df, weight) {
     weight <- enquo(weight)
     summarize_at(df, vars(starts_with("var")), 
                  list(~sum(if_else(. == 1, !! weight, 0)) / sum(!! weight))) 
}

myfun2(data, weighting)

# A tibble: 1 x 3
   vara  varb  varc
  <dbl> <dbl> <dbl>
1 0.701 0.609 0.287
5 Likes

Oh thanks for checking that out @aosmith, that's great news! I tried out the dev version and it indeed runs fine.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.