2

I'm trying to use dplyr::filter() with NSE, but I can't make it work when the filtered variable is as.Date().

Here the function that almost works:

foo <- function(d, f, var) {
  d %>% 
    filter_(paste0(var, ">=", f))
}

d <- data.frame(a=1:10, b=1)
foo(d, f=5, var="a")
#    a b
# 1  5 1
# 2  6 1
# 3  7 1
# 4  8 1
# 5  9 1
# 6 10 1

But if a is a date it won't work:

d <- data.frame(a=seq.Date(as.Date("2019-01-01"), length.out = 7, by="day"), b=1)

foo(d, f=as.Date("2019-01-05"), var="a") # or foo(d, f="2019-01-05", var="a")
#            a b
# 1 2019-01-01 1
# 2 2019-01-02 1
# 3 2019-01-03 1
# 4 2019-01-04 1
# 5 2019-01-05 1
# 6 2019-01-06 1
# 7 2019-01-07 1

Also I've tried with this:

foo2 <- function(d, f, var) {
  d %>% 
    filter(!!var >= f)
}
#foo2(d, f=5, var="a")
#foo2(d, f="2019-01-05", var="a")

Which doensn't work in neither cases, I'm interested in knowing also why foo2 it's not working.

RLave
  • 8,144
  • 3
  • 21
  • 37
  • 1
    Since you are passing the variable name as a string, you'll want something like `ensym()` around `var` in your function. Like `var = ensym(var)` before the `filter()` line. Then things should work for dates if you pass `f` a value via `as.Date()`. – aosmith Jan 24 '19 at 15:34
  • Thank you, this works with `foo2`, care to post as answer? – RLave Jan 24 '19 at 15:37

3 Answers3

1

You don't need !! (rlang::). Enclose var into as.name (or as.symbol) to make into a symbol and then use eval() inside filter to evaluate the object at the function environment:

library(magrittr)
library(dplyr)    

d <- data.frame(a=seq.Date(as.Date("2019-01-01"), length.out = 7, by="day"), b=1)

foo2 <- function(d, f, var) {
  sym <- as.name(var)

  d %>% 
    filter(eval(sym) >= f)
}

Result:

> foo2(d, f="2019-01-05", var="a")
           a b
1 2019-01-05 1
2 2019-01-06 1
3 2019-01-07 1
JdeMello
  • 1,708
  • 15
  • 23
1

try to pass the var argument not as a string. in that case you need to change the function foo to:

foo <- function(d, f, var) {
   var <- enquo(var)
   d %>% 
      filter(!!var >= f)
}
foo(d, f=as.Date("2019-01-05"), var=a)
davide
  • 315
  • 3
  • 10
1

Don't revise your function. Let's focus on the code

foo(d, f = as.Date("2019-01-05"), var = "a")

If the input f is a Date, the statement paste0(var, ">=", f) in your function will return

"a >= 2019-01-05"(It means the column a is greater than and equal to "2019-01-05", which is a character string, not a Date because the outputs of paste() are always characters)

The statement above is not meaningful because character strings are unable to be compared. So you need to transform the Date of input to numeric by unclass() or as.numeric() such as

foo(d, f = unclass(as.Date("2019-01-05")), var = "a")

and the statement will return "a >= 17901", which is a normal logical statement.


Output

foo(d, f = unclass(as.Date("2019-01-05")), var = "a")

#            a b
# 1 2019-01-05 1
# 2 2019-01-06 1
# 3 2019-01-07 1

Notice that "a >= 17901" will succeed because Dates(Column a) can be compared with numerics(17910).

Darren Tsai
  • 32,117
  • 5
  • 21
  • 51