Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using tidy eval for multiple dplyr filter conditions

I'm new to tidy eval and trying to write generic functions- one thing I'm struggling with right now is writing multiple filter conditions for categorical variables. This is what I'm using right now-

create_expr <- function(name, val){
   if(!is.null(val))
     val <- paste0("c('", paste0(val, collapse = "','"), "')")
   paste(name, "%in%", val)
}

my_filter <- function(df, cols, conds){
#   Args: 
#     df: dataframe which is to be filtered
#     cols: list of column names which are to be filtered
#     conds: corresponding values for each column which need to be filtered

cols <- as.list(cols)
conds <- as.list(conds)

args <- mapply(create_expr, cols, conds, SIMPLIFY = F)

if(!length(args))
  stop(cat("No filters provided"))

df <- df %>% filter_(paste(unlist(args), collapse = " & "))
return(df)
}

my_filter(gapminder, cols = list("continent", "country"), 
                     conds = list("Europe", c("Albania", "France")))

I want to know how this could be re-written using tidy eval practices. I've found material on using quos() for multiple arguments but as you can see I have two different lists of arguments here which need to be mapped to each other.

Any help is appreciated, Thanks!

like image 489
Mridul Garg Avatar asked Mar 02 '18 19:03

Mridul Garg


1 Answers

Using the tidyverse, you could re-write that function as

library(dplyr)
library(purrr) # for map2()

my_filter <- function(df, cols, conds){     
  fp <- map2(cols, conds, function(x, y) quo((!!(as.name(x))) %in% !!y))
  filter(df, !!!fp)
}

my_filter(gapminder::gapminder, cols = list("continent", "country"), 
          conds = list("Europe", c("Albania", "France")))

This is calling the equivalent of

filter(gapminder, continent %in% "Europe", country %in% c("Albania", "France"))

The main reason this works is that you can pass multiple arguments to filter() and they are implicitly combined with &. And map2() is just a tidyverse equivalent for mapply with two objects to iterate.

like image 105
MrFlick Avatar answered Oct 26 '22 02:10

MrFlick