Pierre
Pierre

Reputation: 741

variable filter condition

Im aware that this question most likely is a duplicate. I have tried to google this question but Iam not able to find what Iam looking for.

What I would like to do is to filter a dataset based on a user specified condition. E.g.:

df <- 
  data.frame(type = c("a", "a", "b", "b"), 
           value = c(1,5,7,2), stringsAsFactors = F) %>% 
  as_tibble()

cond <- ">"
val <- 2
df %>% filter(value, cond, val)

(i.e. same as df %>% filter(value>2))

If possible I would be able to use this setup both within and outside a function.

Edit:

I was in a bit of a hurry when I asked my question. What I actually wanted was to mutate, not filter, based on a user specified condition. Ronak Shahs answer solved the filter problem and I just made some minor changes regarding the mutate problem. I dont think it is necessary to post a new question as these two are closely related. Therefore I just post a answer regarding the mutate issue (which also answer Tjebo´s comment).

Upvotes: 2

Views: 306

Answers (4)

Pierre
Pierre

Reputation: 741

Please see my edit.

df <- 
  data.frame(type = c("a", "a", "b", "b"), 
           value = c(1,5,7,2), stringsAsFactors = F) %>% 
  as_tibble() %>% 
  mutate(cond = case_when(type == "a" ~ "==", 
                          type == "b" ~ ">="),
         val1 = case_when(type == "a" ~ 1, 
                          type == "b" ~ 3),
         val2 = case_when(type == "a" ~ 6, 
                          type == "b" ~ 7),
         val3 = case_when(type == "a" ~ 9, 
                          type == "b" ~ 0)) 

Based on Ronak Shahs solution

apply_fun <- function(df, col, fun, val, colname = "a") {
  fun1 <- match.fun(fun)
  df <- df %>% mutate(!!colname := fun1({{col}}, val))
  df[, ncol(df)]
}

There are probobly a better final solution, but this is what I came up with.

lst <- list()
for (i in 1:nrow(df)) {
  lst[[i]] <- 
    bind_cols(
    apply_fun(df[i,], df[i,]$value, df[i,]$cond, df[i,]$val1, "type1"), 
    apply_fun(df[i,], df[i,]$value, df[i,]$cond, df[i,]$val2, "type2"),
    apply_fun(df[i,], df[i,]$value, df[i,]$cond, df[i,]$val3, "type3")
  )
}


df %>% bind_cols(., bind_rows(lst))
# A tibble: 4 x 9
  type  value cond   val1  val2  val3 type1 type2 type3
  <chr> <dbl> <chr> <dbl> <dbl> <dbl> <lgl> <lgl> <lgl>
1 a         1 ==        1     6     9 TRUE  FALSE FALSE
2 a         5 ==        1     6     9 FALSE FALSE FALSE
3 b         7 >=        3     7     0 TRUE  TRUE  TRUE 
4 b         2 >=        3     7     0 FALSE FALSE TRUE 

Upvotes: 0

Ronak Shah
Ronak Shah

Reputation: 388982

Another way is to use match.fun to match the function to apply and use NSE to apply it to a column.

library(dplyr)
library(rlang)

apply_fun <- function(df, col, fun, val) {
   fun1 <- match.fun(fun)
   df %>% filter(fun1({{col}}, val))
}

df %>% apply_fun(value, cond, val)

#  type  value
#  <chr> <dbl>
#1 a         5
#2 b         7

df %>% apply_fun(value, cond, 1)
# A tibble: 3 x 2
#  type  value
#  <chr> <dbl>
#1 a         5
#2 b         7
#3 b         2

Upvotes: 4

Peter H.
Peter H.

Reputation: 2164

Another option is:

library(tidyverse)
library(rlang)

df %>% 
  filter_at("value", ~ eval(sym(cond))(.x, val))

Upvotes: 1

Sotos
Sotos

Reputation: 51592

One way is to use eval(parse(text = ...)), i.e.

df %>% 
 filter(eval(parse(text = paste('value', cond, val))))

# A tibble: 2 x 2
#  type  value
#  <chr> <dbl>
#1 a         5
#2 b         7

Upvotes: 2

Related Questions