Reputation: 1134
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
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
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