6

I am writing my function and want to use dplyr's filter() function to select rows of my data frame that satisfy a condition. This is my code:

library(tidyverse)

df <-data.frame(x = sample(1:100, 50), y = rnorm(50), z = sample(1:100,50), w = sample(1:100, 50),
            p = sample(1:100,50))

new <- function(ang,brad,drau){
  df%>%filter(!!drau %in% 1:50)%>%select(ang,brad) -> A
return(A)
}

brand <- c("z","w","p")
lapply(1:3, function(i) new(ang = "x", brad = "y", drau = brand[i]))%>%bind_rows()

Anytime I run this function, it looks like filter doesn't select any rows that satisfy the condition.

How can I make this work?

Update

For some reason, this works when I don't use `%in%, as in;

new <- function(ang,brad,drau){
  df%>%filter(!!drau > 50)%>%select(ang,brad) -> A
return(A)
}

lapply(1:3, function(i) new(ang = "x", brad = "y", drau = brand[i]))%>%bind_rows()

However, the results are the same for every loop. Why is this so? and also why can't I use %in%.

Hong Ooi
  • 56,353
  • 13
  • 134
  • 187
Kay
  • 2,057
  • 3
  • 20
  • 29

4 Answers4

9

I agree with @hrbrmstr's standard evaluation solution. As suggested by @hadley today here's NSE solution:

library(tidyverse)

df <-data.frame(x = sample(1:100, 50), 
                y = rnorm(50), 
                z = sample(1:100,50), 
                w = sample(1:100, 50),
                p = sample(1:100,50))

new <- function(ang, brad, drau){
  ang  <- enquo(ang)
  brad <- enquo(brad)
  drau <- enquo(drau)

  df %>% filter(UQ(drau) %in% 1:50) %>%
    select(UQ(ang),UQ(brad)) 
}

brand <- c("z","w","p")
brand <- rlang::syms(brand)

map_df(brand, ~new(ang = x, brad = y, drau = UQ(.x)))
dmi3kno
  • 2,943
  • 17
  • 31
  • I want to mention that `UQ` is now deprecated. Please have a look at double-moustache operator for modern day programming with dplyr. I recommend watching any recent talk by Lionel Henry. – dmi3kno Feb 27 '20 at 07:51
8

This appears to do what you want (but it needs confirmation by you):

library(tidyverse)
library(rlang)

set.seed(1492)

xdf <- data_frame(
  x = sample(1:100, 50),
  y = rnorm(50), 
  z = sample(1:100,50), 
  w = sample(1:100, 50),
  p = sample(1:100,50)
)

new_df <- function(ang, brad, drau) {
  drau <- sym(drau)
  filter(xdf, UQE(drau) %in% 1:50) %>% 
    select(ang, brad)
}

brand <- c("z", "w", "p")

map_df(brand, ~new_df(ang = "x", brad = "y", drau = .x))

Despite there being a plethora of "official" "tidyverse" examples using df, it's a function in the stats pkg and I try to avoid using it anymore.

Since you're using the tidyverse, might as well take advantage of map_df() from purrr.

hrbrmstr
  • 77,368
  • 11
  • 139
  • 205
  • I don't understand "plethora of "official" "tidyverse" examples using `df`, it's a function in the `stats` pkg". I don't see any `stats` function. I guess you meant `base::lapply`? – dpprdan Jul 21 '17 at 07:37
  • 3
    @dpprdan `df` returns the density of the F distribution and comes from the stats package. I agree with @hrbrmstr and I also stopped using `df` for naming – aurelien Jul 21 '17 at 09:07
  • 1
    You can get away without loading `library(rlang)` if you replace `rlang::sym()` with base function `as.name()` – dmi3kno Jul 21 '17 at 19:12
  • This answer will not work anymore as `UQE` is now deprecated. Please check my answer or the other. – Kay Apr 08 '20 at 22:12
1

With UQE now deprecated, the accepted answer for this question will not work. This answer should work. The only change here is !!sym()

library(rlang)
library(tidyverse)

df <-data.frame(x = sample(1:100, 50), y = rnorm(50), z = sample(1:100,50), w = sample(1:100, 50),
                p = sample(1:100,50))

new <- function(ang,brad,drau){
  df%>%filter(!!sym(drau) %in% 1:50)%>%select(!!sym(ang), !!sym(brad)) -> A
  return(A)
}

brand <- c("z","w","p")
lapply(1:3, function(i) new(ang = "x", brad = "y", drau = brand[i]))%>%bind_rows()

If you want do not want to pass your function arguments as strings, use !!enquo() instead.

new <- function(ang,brad,drau){
  df%>%filter(!!enquo(drau) %in% 1:50)%>%select(!!enquo(ang), !!enquo(brad)) -> A
  return(A)
}

> head(new(ang = x, brad = y, drau = z))
    x           y
1  44  0.47702540
2  84 -1.09670409
3  59 -0.20556334
4  81 -0.46306635
5  93  1.36845485
6   8  0.37392587
Kay
  • 2,057
  • 3
  • 20
  • 29
1

I had a similar issue, and the simple fix that worked for me was to use the ".data" pronoun within the "filter" verb, specifically in this context:

filter(.data[[drau]] %in% 1:50)

More info here: https://tinyheero.github.io/2020/03/01/use-data-env-pronouns-tidyverse.html Also recent talks by Lionel Henry on Rstudio's youtube channel are helpful too.

library(tidyverse)

df <-data.frame(x = sample(1:100, 50), y = rnorm(50), z = sample(1:100,50), w = sample(1:100, 50),
                p = sample(1:100,50))

new <- function(ang,brad,drau){
  df%>%filter(.data[[drau]] %in% 1:50)%>%select(ang,brad) -> A
  return(A)
}

brand <- c("z","w","p")
lapply(1:3, function(i) new(ang = "x", brad = "y", drau = brand[i]))%>%bind_rows()

Hope someone finds some use in this.

Dexterity
  • 11
  • 1