0

I have a data frame:

 a <- c(1,2,3)
 b <- c(4,5,6)
 c <- c(7,8,9)
 d <- c(10,11,12)
 e <- data.frame(a=a,b=b,c=c,d=d)
 #e
 #  a b c  d
 #1 1 4 7 10
 #2 2 5 8 11
 #3 3 6 9 12

I would like to specify a list of conditions to filter the data frame on (I realize that the example is silly, but bear with me).

I would like to create a function where I can enter a varying number of conditions and get as output the rows that the condition applies to.

So for example, if I enter a list:

condition <- list('a'=3,'b'=6)

Then that would mean that I want:

e[e[,'a'] == 3 & e[,'b'] == 6,]
#  a b c  d
#3 3 6 9 12

I tried to enter this into a string

st <- "e[,'a'] == 3 & e[,'b'] == 6"
e[get(st),]

This produces an error.

I would imagine that something could be generated using an apply function but I really have no idea how to do it. Any hint would be appreciated.

Michal
  • 1,863
  • 7
  • 30
  • 50

4 Answers4

3

You can use expressions, something like

conds <- list(W = as.name("a"), X = 3, Y = as.name("b"), Z = 6)
expr <- substitute(W == X & Y == Z, env = conds)    
e[with(e, eval(expr)), ]
#   a b c  d
# 3 3 6 9 12

You can also use Hadley's lazyeval package which is fun and simple to use. And we can create a function that acts similar to dplyr::filter

library(lazyeval)

fun <- function(.data, ...) {
    dots <- lazy_dots(...)
    .data[lazy_eval(dots, data = .data)[[1]], ]
}

fun(e, a == 3 & b == 6)
#   a b c  d
# 3 3 6 9 12
Rich Scriven
  • 97,041
  • 11
  • 181
  • 245
2

The way to get your st string example to work is eval(parse()), but that's not generally recommended. (See here for details on why it's a bad idea: What specifically are the dangers of eval(parse(...))?.)

> e[eval(parse(text = st)), ]
  a b c  d
3 3 6 9 12

If you could share a bit more context, it would be easier to come up with alternate solutions. What is your list of conditions being based on?

You can, as @thelatemail commented, think of this as a join operation (with your condition working because a data.frame is a list), making it a data.frame explicitly:

condition.df <- data.frame(a = 3, b = 6)
merge(e, condition.df)
Community
  • 1
  • 1
Gregor Thomas
  • 136,190
  • 20
  • 167
  • 294
2
 e[ eval( parse(text= paste(names(condition), "==", condition, 
                                      sep=" ", collapse="&")) ) , ]
#---------
  a b c  d
3 3 6 9 12

You could as an envir=e argument to eval to make it a bit "safer".

IRTFM
  • 258,963
  • 21
  • 364
  • 487
1

You can add arguments to the function if necessary, but this replicates what you're trying to do.

    foo <- function(data, column1, x, column2, y){
      out <- data[data[, column1] == x & data[, column2] == y, ]
      return(out)
    }

    foo(dF, "a", 3, "b", 6)
Steven
  • 3,238
  • 21
  • 50
  • 1
    This is a nice small example, but it doesn't generalize up very well, and it would require some adaptation to make it work with anything other than exactly `n` conditions (with `n = 2` as written). – Gregor Thomas Dec 19 '14 at 02:07
  • Oh, I completely agree. However, it's also very simple, easy to scale up, and, to someone that may be starting out with native R functions, be a great learning tool. That said, I'll definitely be incorporating the accepted answer into my programming, too. – Steven Dec 19 '14 at 20:51