Home > front end >  r successive filtering with n arguments in a list
r successive filtering with n arguments in a list

Time:03-21

I am trying to apply successive filters on a dataframe without knowing in advance the number of filter or their arguments. Arguments are stocked in a list. With 1 or 2 filters, i can do it with purrr.

For instance with 2 filters :

require(tidyverse) 

data("iris")
head(iris)

f2 <- list("Species" = "virginica", "Sepal.Length" = c(5.8, 6.3))
iris_f2 <- map2_df(.x = f2[[1]],
                   .y = f2[[2]],
                   .f = ~{
                    iris %>%
                      filter(get(names(f2)[1]) %in% .x,
                             get(names(f2)[2]) %in% .y)
                    })


# With 3 filters or more, I am completely stuck !

f3 <- list("Species" = "virginica", "Sepal.Length" = c(5.8, 6.3), "Sepal.Width" = 2.7)


I would like to generalize my code so that it applies successive filters with n arguments in a list (n can be 1, or 2 as in my example or more).

Ideally, I would like to know how to do it with purrr but I am also interested in loop-based solutions.

CodePudding user response:

Here is one way that uses call() to construct defused expressions that can be spliced inside of filter().

library(purrr)
library(dplyr)
    
fns <- imap(f3, ~ call(if (length(.x) == 1) "==" else "%in%", sym(.y), .x))

Which gives the following:

$Species
Species == "virginica"

$Sepal.Length
Sepal.Length %in% c(5.8, 6.3)

$Sepal.Width
Sepal.Width == 2.7

However, the names cause an issue when spliced, so it needs to be unnamed before use:

iris %>%
  filter(!!!unname(fns))

  Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
1          5.8         2.7          5.1         1.9 virginica
2          6.3         2.7          4.9         1.8 virginica
3          5.8         2.7          5.1         1.9 virginica
  • Related