John M
John M

Reputation: 1134

Throw error when not in dplyr tidyeval

I'm using dplyr and trying out tidy evaluation. I'm confused on how to check to make sure someone put in a bare object versus a string for NSE. For example, I would like to filter on non-missing data:


library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
df = data_frame(
  myvar = c(rep("yes", 2), NA)
)
myfun <- function(x){
  x = enquo(x)
  num = df %>%
    filter(!is.na( !! x)) 
  return(num)
}

myfun(myvar)
#> # A tibble: 2 x 1
#>   myvar
#>   <chr>
#> 1 yes  
#> 2 yes

I would like the string equivalent to fail if possible. This currently gives the "wrong" result as is.na("myvar") is never FALSE.

myfun("myvar") # wrong result
#> # A tibble: 3 x 1
#>   myvar
#>   <chr>
#> 1 yes  
#> 2 yes  
#> 3 <NA>

After looking at What is the tidyeval way of using dplyr::filter?, it seems like filter_at will allow for both scenarios to work fine:

myfun <- function(x){
  x = enquo(x)
  num = df %>%
    filter_at(vars( !! x), all_vars(!is.na(.)))
  return(num)
}

myfun(myvar)
#> # A tibble: 2 x 1
#>   myvar
#>   <chr>
#> 1 yes  
#> 2 yes
myfun("myvar") # correct result
#> # A tibble: 2 x 1
#>   myvar
#>   <chr>
#> 1 yes  
#> 2 yes

But is there a way to have myfun("myvar") fail? I can't use colnames() and if statements as the unquoted expressions fail in that unless maybe using as.name.

Upvotes: 4

Views: 102

Answers (2)

MrFlick
MrFlick

Reputation: 206243

You can test for string literals with something like this

myfun <- function(x){
  x = enquo(x)
  stopifnot(!is.character(rlang::f_rhs(x)))
  num = df %>%
    filter(!is.na( !! x)) 
  return(num)
}

Since quosures are quite similar to formulas, the rlang::f_rhs part extracts the "thing" passed in so you can check what kind of language element it is. Perhaps instead of checking for a string, you might just want to make sure it's a symbol. You can do that with

myfun <- function(x){
  x = enquo(x)
  stopifnot(rlang::quo_is_symbol(x))
  num = df %>%
    filter(!is.na( !! x)) 
  return(num)
}

Then these do what you want

myfun(myvar) #works
myfun("myvar") #error

Upvotes: 3

divibisan
divibisan

Reputation: 12155

You could test x in your function before unquoting it. If you pass in x as a quoted string, then its length = 1 and the function will return NA or whatever failure condition you desire. If you (correctly) pass in an unquoted variable name, then length will fail (with Error: object 'knockdown' not found) but the error will be silenced by try() and the function will proceed as normal.

myfun <- function(x){
try({if (length(x) == 1) return(NA)}, silent = TRUE)
x = enquo(x)
  num = df %>%
    filter_at(vars( !! x), all_vars(!is.na(.)))
  return(num)
}

myfun(myvar)
# A tibble: 2 x 1
  myvar
  <chr>
1 yes  
2 yes  

myfun("myvar")
[1] NA

Upvotes: 0

Related Questions