6

Is there a way to do filter with a string as parameter without using eval(parse())?

library("dplyr")

subset <- "carb == 4"
subset_df <- mtcars %>% filter(eval(parse(text = subset)))
NelsonGon
  • 13,015
  • 7
  • 27
  • 57
hxalchemy
  • 366
  • 1
  • 10
  • 2
    Can you explain why you want this? There are simple ways like writing your own function `evalparse <- function(x) eval(parse(text = x), envir = parent.frame())`, and tidyverse alternatives as in the answer below. Are those what you want? – user2554330 Sep 04 '21 at 16:14
  • I have read numerous posts on why you shouldn't use eval parse, which I agree with. But being able to pass in text along with dplyr makes it so much more dynamic, so my goal was to achieve the same level of dynamic code with security which is lost with eval parse – hxalchemy Sep 07 '21 at 18:02
  • I suspect all of the suggestions here have the same security issues as `eval(parse())`. – user2554330 Sep 07 '21 at 19:06

3 Answers3

7

1) rlang If what you are asking is whether there are counterparts to eval/parse in the tidyverse then, yes, there are. You will also need rlang which is already used by dplyr but dplyr does not export the functions needed so use a library statement to load it.

library(dplyr)
library(rlang)

subset <- "carb == 4"
mtcars %>% filter(eval_tidy(parse_expr(subset)))

giving:

                     mpg cyl  disp  hp drat    wt  qsec vs am gear carb
Mazda RX4           21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag       21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
Duster 360          14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
Merc 280            19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
Merc 280C           17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4
Cadillac Fleetwood  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4
Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4
Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4
Camaro Z28          13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
Ford Pantera L      15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4

1a) This also works:

mtcars %>% filter(!!parse_quo(subset, .GlobalEnv))

2) sqldf If you are looking for a way to do this without using eval/parse or any direct alternative to it and it is not required to use the tidyverse then sqldf can do that provided subset contains valid SQL which in the case of the question it does.

library(sqldf)
subset <- "carb == 4"
fn$sqldf("select * from mtcars where $subset")

giving:

    mpg cyl  disp  hp drat    wt  qsec vs am gear carb
1  21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
2  21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
3  14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
4  19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
5  17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4
6  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4
7  10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4
8  14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4
9  13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
10 15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4

2a) This can also be written in terms of pipes like this:

mtcars %>% { fn$sqldf("select * from '.' where $subset") }
G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
3

It is deprecated, but you could use filter_

Code

mtcars %>%
  filter_("carb == 4")

Output

                     mpg cyl  disp  hp drat    wt  qsec vs am gear carb
Mazda RX4           21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag       21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
Duster 360          14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
Merc 280            19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
Merc 280C           17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4
Cadillac Fleetwood  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4
Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4
Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4
Camaro Z28          13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
Ford Pantera L      15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4
Vinícius Félix
  • 8,448
  • 6
  • 16
  • 32
0

If we can use a function, then we can pass it as unquoted and evaluate it in {{}} i.e. we don't need another package nor do any eval/parse

library(dplyr)
f1 <- function(data, expr) {
       data %>%
         filter({{expr}})
}

-testing

> f1(mtcars, carb == 4)
                   mpg cyl  disp  hp drat    wt  qsec vs am gear carb
Mazda RX4           21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag       21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
Duster 360          14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
Merc 280            19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
Merc 280C           17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4
Cadillac Fleetwood  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4
Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4
Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4
Camaro Z28          13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
Ford Pantera L      15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4

Also, if we want to pass from an object, instead of creating a string, quote it

expr1 <- quote(carb == 4)
f1(mtcars, !!expr1)
                     mpg cyl  disp  hp drat    wt  qsec vs am gear carb
Mazda RX4           21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag       21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
Duster 360          14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
Merc 280            19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
Merc 280C           17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4
Cadillac Fleetwood  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4
Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4
Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4
Camaro Z28          13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
Ford Pantera L      15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4
akrun
  • 874,273
  • 37
  • 540
  • 662