0

I'm working with dplyr and created code to compute new data that is plotted with ggplot.

I want to create a function with this code. It should take a name of a column of the data frame that is manipulated by dplyr. However, trying to work with columnnames does not work. Please consider the minimal example below:

df <- data.frame(A = seq(-5, 5, 1), B = seq(0,10,1))

library(dplyr)
foo <- function (x) {
         df %>%
            filter(x < 1)
}

foo(B)

Error in filter_impl(.data, dots(...), environment()) : 
  object 'B' not found 

Is there any solution to use the name of a column as a function argument?

jnshsrs
  • 337
  • 4
  • 11
  • 1
    Could you make your question [reproducible](http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example)? At this moment your are giving a dataframe with all (two) values above 1. – Jaap Sep 23 '14 at 08:08
  • Thanks Jaap for your advice, I edited the df to ensure reproducibility – jnshsrs Sep 23 '14 at 08:18
  • 3
    I remember a similar question which was answered by @Richard Scriven. I think you need to write something like `foo <- function(x,...)filter(x,...)` I have the following right now. Not sure if I am writing something correct. But the outcome seems to be right.`foo <- function (x,...) filter(x,...);foo(df, B < 1)` – jazzurro Sep 23 '14 at 08:22
  • @jazzurro, unfortunately I can't reproduce your advices. Maybe you can contribute reproducible example. – jnshsrs Sep 23 '14 at 09:25
  • @jnshsrs I used your df and ran the code. R returns the first row. Could you run the code with your df on your machine? – jazzurro Sep 23 '14 at 09:35
  • @jazzurro, OK, I could run the code. I try to implement this in my function and give a feedback of the result. Thanks so far! – jnshsrs Sep 23 '14 at 09:53
  • @jnshsrs OK, I am glad to hear that. I will leave my comment as an answer then. – jazzurro Sep 23 '14 at 10:01

2 Answers2

6

If you want to create a function which accepts the string "B" as an argument (as in you question's title)

foo_string <- function (x) {
         eval(substitute(df %>% filter(xx < 1),list(xx=as.name(x))))
}
foo_string("B")

If you want to create a function which accepts captures B as an argument (as in dplyr)

foo_nse <- function (x) {
         # capture the argument without evaluating it
         x <- substitute(x)
         eval(substitute(df %>% filter(xx < 1),list(xx=x)))
}
foo_nse(B)

You can find more information in Advanced R

Edit

dplyr makes things easier in version 0.3. Functions with suffixes "_" accept a string or an expression as an argument

 foo_string <- function (x) {
             # construct the string
             string <- paste(x,"< 1")
             # use filter_ instead of filter
             df %>% filter_(string)
    }
foo_string("B")
 foo_nse <- function (x) {
             # capture the argument without evaluating it
             x <- substitute(x)
             # construct the expression
             expression <- lazyeval::interp(quote(xx < 1), xx = x)
             # use filter_ instead of filter
             df %>% filter_(expression)
    }
foo_nse(B)

You can find more information in this vignette

Matthew
  • 2,628
  • 1
  • 20
  • 35
1

I remember a similar question which was answered by @Richard Scriven. I think you need to write something like this.

foo <- function(x,...)filter(x,...) 

What @Richard Scriven mentioned was that you need to use ... here. If you type ?dplyr, you will be able to find this: filter(.data, ...) I think you replace .data with x or whatever. If you want to pick up rows which have values smaller than 1 in B in your df, it will be like this.

foo <- function (x,...) filter(x,...)
foo(df, B < 1)
jazzurro
  • 23,179
  • 35
  • 66
  • 76
  • I must be getting old. I have no recollection of this. But +1, cuz it works! – Rich Scriven Sep 23 '14 at 18:28
  • @RichardScriven I can be wrong. But, I think it was you who answered a similar question using `select()`. That was my inspiration. The code above works, but I was surprised when I saw that. – jazzurro Sep 24 '14 at 00:49