I would like to write a wrapper around a custom function that takes some vectors as input (like: mtcars$hp, mtcars$am etc.) to take input as data frame name (as data parameter, eg.: mtcars) and variable names (like: hp and am), as usual in most standard function.
But I have some problems, my proposed ‘demo’ function (a wrapper around mean does not work.
Code:
f <- function(x, data=NULL) {
if (!missing(data)) {
with(data, mean(x))
} else {
mean(x)
}
}
Running against a vector works of course:
> f(mtcars$hp)
[1] 146.69
But with fails unfortunatelly:
> f(hp, mtcars)
Error in with(d, mean(x)) : object 'hp' not found
While in global environment/without my custom function works right:
> with(mtcars, mean(hp))
[1] 146.69
I have tried to do some experiment with substitute, deparse and others, but without any success. Any hint would be welcomed!
Here’s the key piece of the puzzle:
See @Andrie’s link to @Hadley’s document for an explanation of why it works. See @Hadley’s note for a critical caveat: f() cannot be run from inside another function.
Basically R uses lazy evaluation (e.g. it doesn’t evaluate things until they’re actually used). So you can get away with passing it
hpbecause it remains an unevaluated symbol until it appears somewhere. Sincematch.callgrabs it as a symbol and waits to evaluate it, all is well.Then
evalevaluates it in the specified environment. According to?eval, the second argument represents:Therefore you’re in good shape with either NULL (if you’re not passing a data.frame) or a data.frame.
Proof of lazy evaluation is that this doesn’t return an error (since x is never used in the function):