Search code examples
rdplyrnsetidyeval

R: Using a string as an argument to mutate verb in dplyr


I am building a shiny app which needs to allow users to define new variables for plotting. Specifically I want to allow users to define an expression to be used in mutate verb. The server receives the expression as text and I am wondering how to make mutate execute it in dplyr 0.7. I can make it work (partially) using mutate_ but it is deprecated now. It also defines the new column name as the entire expression rather than the new variable

Here is a reproducible example:

input_from_shiny <- "Petal.ratio = Petal.Length/Petal.Width"
iris_mutated <- iris %>% mutate_(input_from_shiny)

This gives the following

> head(iris_mutated)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species Petal.ratio = Petal.Length/Petal.Width
1          5.1         3.5          1.4         0.2  setosa                                   7.00
2          4.9         3.0          1.4         0.2  setosa                                   7.00
3          4.7         3.2          1.3         0.2  setosa                                   6.50
4          4.6         3.1          1.5         0.2  setosa                                   7.50
5          5.0         3.6          1.4         0.2  setosa                                   7.00
6          5.4         3.9          1.7         0.4  setosa                                   4.25

Technically, I can use regular expression to extract new variable name from the string and rename the new column accordingly, but I am wondering what is the correct way to implement it using latest dplyr version (was reading https://cran.r-project.org/web/packages/dplyr/vignettes/programming.html, but could not figure it out)


Solution

  • We can use rlang::parse_quosure() together with !! (bang bang) to produce the same result:

    • parse_quosure: parses the supplied string and converts it into a quosure

    • !!: unquotes a quosure so it can be evaluated by tidyeval verbs

    Note that parse_quosure() was soft-deprecated and renamed to parse_quo() in rlang 0.2.0 per its documentation. If we use parse_quo(), we need to specify the environment for the quosures e.g. parse_quo(input_from_shiny, env = caller_env())

    library(rlang)
    library(tidyverse)
    
    input_from_shiny <- "Petal.ratio = Petal.Length/Petal.Width"
    iris_mutated <- iris %>% mutate_(input_from_shiny)
    
    iris_mutated2 <- iris %>% 
      mutate(!!parse_quosure(input_from_shiny))
    head(iris_mutated2)
    
    #>   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
    #> 1          5.1         3.5          1.4         0.2  setosa
    #> 2          4.9         3.0          1.4         0.2  setosa
    #> 3          4.7         3.2          1.3         0.2  setosa
    #> 4          4.6         3.1          1.5         0.2  setosa
    #> 5          5.0         3.6          1.4         0.2  setosa
    #> 6          5.4         3.9          1.7         0.4  setosa
    #>   Petal.ratio = Petal.Length/Petal.Width
    #> 1                                   7.00
    #> 2                                   7.00
    #> 3                                   6.50
    #> 4                                   7.50
    #> 5                                   7.00
    #> 6                                   4.25
    
    
    identical(iris_mutated, iris_mutated2)
    #> [1] TRUE
    

    Edit: to separate LHS & RHS

    lhs <- "Petal.ratio"
    rhs <- "Petal.Length/Petal.Width"
    
    iris_mutated3 <- iris %>% 
      mutate(!!lhs := !!parse_quosure(rhs))
    head(iris_mutated3)
    
    > head(iris_mutated3)
      Sepal.Length Sepal.Width Petal.Length Petal.Width Species
    1          5.1         3.5          1.4         0.2  setosa
    2          4.9         3.0          1.4         0.2  setosa
    3          4.7         3.2          1.3         0.2  setosa
    4          4.6         3.1          1.5         0.2  setosa
    5          5.0         3.6          1.4         0.2  setosa
    6          5.4         3.9          1.7         0.4  setosa
      Petal.ratio
    1        7.00
    2        7.00
    3        6.50
    4        7.50
    5        7.00
    6        4.25
    

    Created on 2018-03-24 by the reprex package (v0.2.0).