rdplyrr-glue

Mutate using glue in a user defined function


I would like to change the values in a specific column to include information from another column using the glue function.

I do it normally like this:

library(glue)
library(dplyr)
df = data.frame(x = c("Banana","Apple","Melon"),
                y = c(10,15,27),
                z = rep(c("something_else"),3))
df %>%
  mutate(x = glue("{x} ({y})"))

The output:

#>             x  y              z
#> 1 Banana (10) 10 something_else
#> 2  Apple (15) 15 something_else
#> 3  Melon (27) 27 something_else

My proplem arises when I try to do the same thing using the dataframe and the column names as inputs in a user defined function.

My intitial instict is to pass the inputs using double curly braces in conjunction with the glue function, but this results in an error.

concatenate_value_to_string <- function(tbl,var1,var2) {
  tbl %>%
    mutate({{var1}} := glue("{{{var1}}} ({{{var2}}})"))
}

concatenate_value_to_string(df,x,y)
#> Error in UseMethod("mutate"): no applicable method for 'mutate' applied to an object of class "function"

Created on 2021-08-02 by the reprex package (v2.0.0)

Clearly triple curly braces are not the solution here, could anyone help me out?

Thank you.


Solution

  • You could use dplyr::pull():

    concatenate_value_to_string <- function(tbl,var1,var2) {
      tbl %>%
        mutate({{var1}} :=  glue("{pull(., {{var1}})} ({pull(., {{var2}})})"))
    }
    
    concatenate_value_to_string(df,x,y)
    
    concatenate_value_to_string(df,x,y)
    #>             x  y              z
    #> 1 Banana (10) 10 something_else
    #> 2  Apple (15) 15 something_else
    #> 3  Melon (27) 27 something_else
    

    Or eval(rlang::expr()), where we first build the symbol from the input then evaluate it in the context of the data frame.

    concatenate_value_to_string <- function(tbl,var1,var2) {
      tbl %>%
        mutate({{var1}} :=  glue("{eval(expr({{var1}}))} ({eval(expr({{var2}}))})"))
    }
    
    concatenate_value_to_string(df,x,y)
    #>             x  y              z
    #> 1 Banana (10) 10 something_else
    #> 2  Apple (15) 15 something_else
    #> 3  Melon (27) 27 something_else
    

    What you tried doesn't work because mutate() doesn't substitute {{foo}} if it's part of a string, in the solutions above, pull() or expr() do it.

    Personally I'd rather use sprintf() in this case:

    concatenate_value_to_string <- function(tbl,var1,var2) {
      tbl %>%
        mutate({{var1}} :=  sprintf("%s (%s)", {{var1}}, {{var2}}))
    }
    
    concatenate_value_to_string(df,x,y)
    #>             x  y              z
    #> 1 Banana (10) 10 something_else
    #> 2  Apple (15) 15 something_else
    #> 3  Melon (27) 27 something_else