Search code examples
rdplyrtidyverserlangquasiquotes

using quasiquotation with `$` operator


I am trying to use rlang to write custom functions. Although I can do this when functions involve data argument, I have trouble how to use quasiquotations properly when the functions require vectors and I need to use the $ operator.

Here is a toy example-

library(tidyverse)

# proper implementation
tryfn <- function(data, x, y) {
  # creating a dataframe
  data <-
    dplyr::select(
      .data = data,
      !!rlang::enquo(x),
      !!rlang::enquo(y)
    ) %>% # dropping unused levels
    dplyr::mutate(.data = ., 
                  !!rlang::enquo(x) := droplevels(as.factor(!!rlang::enquo(x))))

  # checking if data is getting imported properly
  print(data)

  # figuring out number of levels in the grouping factor
  return(length(levels(data$`!!rlang::enquo(x)`))[[1]])
}

# using the function
tryfn(ggplot2::msleep, vore, brainwt)

#> # A tibble: 83 x 2
#>    vore   brainwt
#>    <fct>    <dbl>
#>  1 carni NA      
#>  2 omni   0.0155 
#>  3 herbi NA      
#>  4 omni   0.00029
#>  5 herbi  0.423  
#>  6 herbi NA      
#>  7 carni NA      
#>  8 <NA>  NA      
#>  9 carni  0.07   
#> 10 herbi  0.0982 
#> # ... with 73 more rows

#> Warning: Unknown or uninitialised column: '!!rlang::enquo(x)'.
#> [1] 0

As can be seen here, the data is getting imported properly, but the return value is incorrect because I don't how to use quasiquotation in the context of $ operator. How can I do this?


Solution

  • We can convert to the character class with as_name and extract with [[. To avoid repeating conversion with enquo, do it in once, store in an identifier and reuse

    tryfn <- function(data, x, y) {
     x <- rlang::enquo(x)
     y <- rlang::enquo(y)
      # creating a dataframe
      data <-
        dplyr::select(
          .data = data,
          !!x,
          !!y
        ) %>% # dropping unused levels
        dplyr::mutate(.data = ., 
                      !!x := droplevels(as.factor(!!x)))
    
      # checking if data is getting imported properly
      print(data)
    
      # figuring out number of levels in the grouping factor
      return(length(levels(data[[rlang::as_name(x)]]))[[1]])
    
    }
    

    -testing

    tryfn(ggplot2::msleep, vore, brainwt)
    # A tibble: 83 x 2
    #   vore   brainwt
    #   <fct>    <dbl>
    # 1 carni NA      
    # 2 omni   0.0155 
    # 3 herbi NA      
    # 4 omni   0.00029
    # 5 herbi  0.423  
    # 6 herbi NA      
    # 7 carni NA      
    # 8 <NA>  NA      
    # 9 carni  0.07   
    #10 herbi  0.0982 
    # … with 73 more rows
    #[1] 4