4

I am trying to make a function that takes in a dataframe and adds a new variable which is the name of the input dataframe. For example:

foo <- data.frame(x = 1:10, y = rep(c("a", "b")))
mutate(foo, id = "foo") 

This seemed relatively trivial to accomplish, since this works as it does here:

var_as_string <- function(x) {deparse(substitute(x))}

var_as_string(foo)

Result:
[1] "foo"

I figured this would as well, but it doesn't:

add_id <- function(x) {
    mutate(x, id = deparse(substitute(x)))
}

add_id(foo)

Result:
   zz yy id
1   1  a  x
2   2  b  x
3   3  a  x
4   4  b  x
5   5  a  x
6   6  b  x
7   7  a  x
8   8  b  x
9   9  a  x
10 10  b  x

As a test, I tried this and it doesn't work as I thought it would either:

var_as_string2 <- function(x) {
  var_string <- var_as_string(x)
  var_string(x)
}

var_as_string2(foo)

Result:
[1] "x"

Is there a solution to this? Am I overlooking something here or do I have a fundamental misunderstanding of functions in R?

jdesilvio
  • 1,794
  • 4
  • 22
  • 38
  • `mutate` can be from `dplyr` or `plyr`. I assume you want a solution based on`dplyr`? – akrun Apr 10 '15 at 14:49
  • Sorry about that, I was using `plyr`...the `mutate` part works fine, it that the variable name does not get assigned as the `id` when `deparse(substitute(x))` is used inside a defined function – jdesilvio Apr 10 '15 at 14:53
  • By creating the same way as @konvas's function, the `deparse(substitute(` worked for me. But, I was using `dplyr`. i.e. `add_id <- function(x) {s <- deparse(substitute(x)); mutate(x, id = s)}` – akrun Apr 10 '15 at 15:29

1 Answers1

3

There is probably a better 1-line way of achieving this using lazyeval but this seems to work

add_id <- function(x) {
    s <- as.character(substitute(x))
    mutate(x, id = s)
}

add_id(foo)

(the idea compared to what you were trying to do is that you have to evaluate s outside the mutate call)

konvas
  • 14,126
  • 2
  • 40
  • 46
  • @akrun Good point, if the OP is using `plyr` as described in comments, better stick with `substitute` – konvas Apr 10 '15 at 15:11
  • 1
    @akrun lazyeval was designed for/alongside dplyr but it’s fundamentally a general-purpose replacement for `substitute` when working with unevaluated arguments. – Konrad Rudolph Apr 10 '15 at 15:12
  • @Konrad: I tried to use lazyeval - `add_id_lazy <- function(x) { mutate(x, id = interp(~name, name = as.character(substitute(x)))) }` but this isn't working...any idea why? – jdesilvio Apr 10 '15 at 15:41
  • @Dixon11111 `f = function (x) eval(interp(quote(mutate(x, id = y)), y = deparse(substitute(x))))` — The point is that you use `interp` to inject the unevaluated argument name into the `mutate` expression, and then evaluate it. Alternatively, here’s a simpler solution without `lazyeval` (`bquote` works just fine in place of `interp`): `f = function (x) eval(bquote(mutate(x, id = .(deparse(substitute(x))))))` – Konrad Rudolph Apr 10 '15 at 15:53