kowa
kowa

Reputation: 89

How to filter a dataframe with a character vector

I'm trying to filter a data.frame with filter() function from the package dplyr. The main problem here is that I want to use a vector for the conditions.

For example

library(dplyr)
conditions <- c("Sepal.Width<3.2","Species==setosa")
DATA <- iris %>%
  filter(conditions) #This doesnt work, of course.

Is there any function that would take

conditions <- c("Sepal.Width<3.2","Species==setosa")

as an input and give me

Sepal.Width<3.2 & Species==setosa

as an output? I though about using eval(parse...) with sapplyand maybe paste0() to add the &, but can't make it work.

Any help would be aprecciated.

Upvotes: 4

Views: 341

Answers (3)

Iroha
Iroha

Reputation: 34751

A tidyeval way would be to use rlang::parse_exprs().

library(dplyr)

conditions <- c("Sepal.Width < 3.2", "Species == 'setosa'")

iris %>%
  filter( !!! rlang::parse_exprs(conditions))

   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1           4.9         3.0          1.4         0.2  setosa
2           4.6         3.1          1.5         0.2  setosa
3           4.4         2.9          1.4         0.2  setosa
4           4.9         3.1          1.5         0.1  setosa
5           4.8         3.0          1.4         0.1  setosa
6           4.3         3.0          1.1         0.1  setosa
7           5.0         3.0          1.6         0.2  setosa
8           4.8         3.1          1.6         0.2  setosa
9           4.9         3.1          1.5         0.2  setosa
10          4.4         3.0          1.3         0.2  setosa
11          4.5         2.3          1.3         0.3  setosa
12          4.8         3.0          1.4         0.3  setosa

Upvotes: 2

s_baldur
s_baldur

Reputation: 33613

Here is a way:

conditions <- c("Sepal.Width<3.2","Species=='setosa'")
# note the small change here:               ↑      ↑
DATA <- iris %>%
  filter(eval(parse(text = paste(conditions, collapse = "&"))))

> DATA
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1           4.9         3.0          1.4         0.2  setosa
2           4.6         3.1          1.5         0.2  setosa
3           4.4         2.9          1.4         0.2  setosa
4           4.9         3.1          1.5         0.1  setosa
5           4.8         3.0          1.4         0.1  setosa
6           4.3         3.0          1.1         0.1  setosa
7           5.0         3.0          1.6         0.2  setosa
8           4.8         3.1          1.6         0.2  setosa
9           4.9         3.1          1.5         0.2  setosa
10          4.4         3.0          1.3         0.2  setosa
11          4.5         2.3          1.3         0.3  setosa
12          4.8         3.0          1.4         0.3  setosa

Upvotes: 3

tmfmnk
tmfmnk

Reputation: 40171

There are multiple issues. First, you need to quote inside quotation for the second condition:

conditions <- c("Sepal.Width < 3.2", "Species == 'setosa'")

Then, you need to specify the association between the two conditions. Here, I assumed an &. Then you can use eval(parse(...)):

iris %>%
 filter(eval(parse(text = paste(conditions, sep = "&"))))

   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1           5.1         3.5          1.4         0.2  setosa
2           4.9         3.0          1.4         0.2  setosa
3           4.7         3.2          1.3         0.2  setosa
4           4.6         3.1          1.5         0.2  setosa
5           5.0         3.6          1.4         0.2  setosa
6           5.4         3.9          1.7         0.4  setosa
7           4.6         3.4          1.4         0.3  setosa
8           5.0         3.4          1.5         0.2  setosa
9           4.4         2.9          1.4         0.2  setosa
10          4.9         3.1          1.5         0.1  setosa

On the other hand, I think it is always important to quote @Martin Mächler to warn about the potential problems associated with this approach:

The (possibly) only connection is via parse(text = ....) and all good R programmers should know that this is rarely an efficient or safe means to construct expressions (or calls). Rather learn more about substitute(), quote(), and possibly the power of using do.call(substitute, ......).

Upvotes: 4

Related Questions