0

I am trying write a function which creates a new column by multiplying two other columns, with the names of all three columns being given as (string) parameters to the function.

I was thinking I could write something like this:

A <- data.frame(x = 1:5, y = 11:15)

f <- function(x, y, new) {
  x_quo <- rlang::enquo(x)
  y_quo <- rlang::enquo(y)
  new_quo <- rlang::enquo(new)
  A %<>% mutate (!!new_quo := !!x_quo * !!y_quo)
  A
}

f("x", "y", "new")

I was expecting this to be equivalent to running this code:

A <- data.frame(x = 1:5, y = 11:15)
A %<>% mutate (new = x * y);

However, but when I run that first code, I get this error:

 Error: Problem with `mutate()` input `new`.
 x non-numeric argument to binary operator
 i Input `new` is `"x" * "y"`.
 Run `rlang::last_error()` to see where the error occurred. 

What does this error mean? Is there a way to create a function like I've described?

ClownsENT
  • 155
  • 1
  • 8
  • 1
    Could you post a reproducible problem please? It's difficult for community to understand the problem if we can't reproduce it ourselves. See https://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example/5963610#5963610 – geotheory Jun 22 '20 at 09:01
  • 1
    The error message shows you're trying to multiply strings. You need to convert these to language objects before unquoting, either with `sym()` or `parse_expr()` – Lionel Henry Jun 22 '20 at 09:37
  • @geotheory sure! I've changed it to be (what I think is) a reproducible problem, which I think still captures the issue. Let me know if you think I can need to give any more information. – ClownsENT Jun 22 '20 at 09:39
  • @LionelHenry thanks that's working now! Can I mark your answer as correct somehow? (apologies for my ignorance here). – ClownsENT Jun 22 '20 at 09:44

1 Answers1

2

Try to use sym and evaluate it with !!. I would also pass additional data argument to the function.

library(dplyr)
library(rlang)

f <- function(data, x, y, new) {
  data %>% mutate (!!new := !!sym(x) * !!sym(y))
}

A %>% f("x", "y", "new")

#  x  y new
#1 1 11  11
#2 2 12  24
#3 3 13  39
#4 4 14  56
#5 5 15  75

identical(A %>% f("x", "y", "new"), A %>% mutate (new = x * y))
#[1] TRUE
Ronak Shah
  • 377,200
  • 20
  • 156
  • 213
  • Nice answer. By the way the recommended style is now to avoid unquoting, because it is difficult to understand for newcomers. Instead you can use glue syntax on the LHS of `:=` and the `.data` pronoun on the RHS: `mutate("{new}" := .data[[x]] * .data[[y]]))` – Lionel Henry Jun 23 '20 at 10:16