0

I want to be able to dynamically pass search parameters to dplyr's filter function. I can manually do a search like this:

filter(df, sex=='F', country=='USA')

but I need to be able to do this dynamically.

I tried do.call, like so:

do.call('filter',list(df,country=='USA'))

but that gives me an error:

object country not found

If I put country in quotes, I don't get an error but I don't get any results back from filter.

aosmith
  • 34,856
  • 9
  • 84
  • 118
skunkwerk
  • 2,920
  • 2
  • 37
  • 55
  • 2
    Can you give a more specific example of the situation you need this for? You will likely need to switch to using `filter_` for standard evaluation, see [this answer](http://stackoverflow.com/a/26509961/2461552) and this [vignette](https://github.com/hadley/dplyr/blob/master/vignettes/nse.Rmd) for more info. – aosmith Sep 11 '15 at 23:36

3 Answers3

4

Per aosmith suggestion I came up with:

 df <- data.frame(
          sex = sample(c('M','F'),10, replace = TRUE),
          country = sample(c('USA','UK'),10, replace = TRUE)
                 )

 filter_criteria <- ~ country == 'USA'
 do.call(filter_,list(df,filter_criteria))
mr.joshuagordon
  • 754
  • 4
  • 8
  • I upvoted, but I didn't see an obvious elaboration that uses this approach in the situation where there are multiple criteria constructed as expressions with the `~`-function. I tried making a list of such criteria and it erred out from `lazy.eval`'s unwillingness to deal with lists or pairlists. – IRTFM Sep 12 '15 at 17:42
  • To use multiple criteria using `~` with `do.call` would look something like: `do.call(filter_,list(df, .dots = list(filter_criterion1, filter_criterion2)))` – aosmith Sep 12 '15 at 19:37
1

I saw the request as wanting to be able to have multiple criteria. I needed to use character representations of individual criterion and paste with "&" to get this to succeed. Tried several different approaches to using formula objects with consistent failure:

filter_criterion1 <- "country == 'USA'"
filter_criterion2 <- "sex == 'F'"
do.call(filter_, list(df,paste(filter_criterion2, filter_criterion1, sep="&")))
  sex country
1   F     USA
2   F     USA
3   F     USA

Thanks to aosmith for point out that the .dots parameter will accept lists of either character or formula specifications:

  do.call(filter_, list(df,.dots=list(filter_criterion2, filter_criterion1)))
  sex country
1   F     USA
2   F     USA
3   F     USA
IRTFM
  • 258,963
  • 21
  • 364
  • 487
  • 1
    I think I don't have a clear understanding the use of `do.call` in the OP - I get the same results with `filter_(df, .dots = list(filter_criterion1, filter_criterion2))` as with `do.call`, and it can take conditions in quotes or with formula as in @mr.joshuagordon answer. – aosmith Sep 12 '15 at 19:11
1

I recently worked out away to use filter_ in a dynamic situation with Shiny (see that answer here) using package lazyeval. Depending on what you are doing, it could be relevant, although your actual situation may be simpler.

You could do something similar by creating a vector of variables you want to condition with and vector of the same length with the conditions for each variable.

library(lazyeval)

variables = c("country", "sex")
conditions = c("USA", "F")

Then you could loop through the variables/conditions, using interp to create a list of the conditions you want to filter on.

dots = lapply(1:length(variables),
             function(crit) interp(~y == z, 
                                .values = list(y = as.name(variables[crit]), 
                                            z = conditions[crit])))
dots
[[1]]
~country == "USA"
<environment: 0x02eed660>

[[2]]
~sex == "F"
<environment: 0x02c6b388>

Then just use the resulting list in the .dots argument of filter. I'm using @joshuagordon's df here.

filter_(df, .dots = dots)
  sex country
1   F     USA
2   F     USA
3   F     USA
4   F     USA
Community
  • 1
  • 1
aosmith
  • 34,856
  • 9
  • 84
  • 118