Reputation: 741
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
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
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
Reputation: 2164
Another option is:
library(tidyverse)
library(rlang)
df %>%
filter_at("value", ~ eval(sym(cond))(.x, val))
Upvotes: 1
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