Passing a variable name to a function in R
I've noticed that quite a few packages allow you to pass symbol names that may not even be valid in the context where the function is called. I'm wondering how this works and how I can use it in my own code?
Here is an example with ggplot2:
a <- data.frame(x=1:10,y=1:10)
library(ggplot2)
qplot(data=a,x=x,y=y)
x
and y
don't exist in my namespace, but ggplot understands that they are part of the data frame and postpones their evaluation to a context in which they are valid. I've tried doing the same thing:
b <- function(data,name) { within(data,print(name)) }
b(a,x)
However, this fails miserably:
Error in print(name) : object 'x' not found
What am I doing wrong? How does this work?
Note: this is not a duplicate of Pass variable name to a function in r
Solution 1:
I've recently discovered what I think is a better approach to passing variable names.
a <- data.frame(x = 1:10, y = 1:10)
b <- function(df, name){
eval(substitute(name), df)
}
b(a, x)
[1] 1 2 3 4 5 6 7 8 9 10
Update The approach uses non standard evaluation. I began explaining but quickly realized that Hadley Wickham does it much better than I could. Read this http://adv-r.had.co.nz/Computing-on-the-language.html
Solution 2:
You can do this using match.call
for example:
b <- function(data,name) {
## match.call return a call containing the specified arguments
## and the function name also
## I convert it to a list , from which I remove the first element(-1)
## which is the function name
pars <- as.list(match.call()[-1])
data[,as.character(pars$name)]
}
b(mtcars,cyl)
[1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
explanation:
match.call returns a call in which all of the specified arguments are specified by their full names.
So here the output of match.call
is 2 symbols:
b <- function(data,name) {
str(as.list(match.call()[-1])) ## I am using str to get the type and name
}
b(mtcars,cyl)
List of 2
$ data: symbol mtcars
$ name: symbol cyl
So Then I use first symbol mtcars ansd convert the second to a string:
mtcars[,"cyl"]
or equivalent to :
eval(pars$data)[,as.character(pars$name)]
Solution 3:
Very old thread but you can also use the get
command as well. It seems to work better for me.
a <- data.frame(x = 1:10, y = 11:20)
b <- function(df, name){
get(name, df)
}
b(a, "x")
[1] 1 2 3 4 5 6 7 8 9 10