Can we put R expressions and quosures inside tibbles?

rlang

#1

It would be pretty cool if we could put R expressions in tibbles. The use case might be to build an expression for each row of a tibble. It doesn't seem like this is supported though. Expressions can go in lists so I'm not sure why I can't add them in a list column. Am I missing something?

suppressPackageStartupMessages(library(dplyr))
starwars %>% 
     mutate(expr = rlang::expr(a))
#> Error in mutate_impl(.data, dots): Column `expr` is of unsupported type symbol


starwars %>% 
     mutate(expr = rlang::expr(!!name))
#> Error in quos(...): object 'name' not found

#2

You should specify that this a list and then you can store expressions with no issues:

library(tidyverse)
#> ── Attaching packages ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
#> ✔ ggplot2 2.2.1     ✔ purrr   0.2.4
#> ✔ tibble  1.4.2     ✔ dplyr   0.7.4
#> ✔ tidyr   0.8.0     ✔ stringr 1.3.0
#> ✔ readr   1.1.1     ✔ forcats 0.3.0
#> ── Conflicts ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
#> ✖ dplyr::filter() masks stats::filter()
#> ✖ dplyr::lag()    masks stats::lag()
a <- 2L

starwars %>% 
  dplyr::mutate(expr = c(rlang::expr(a))) %>% 
  dplyr::mutate(height_and_expr = purrr::map2_int(height, expr, ~(.x + eval(.y)))) %>% 
  dplyr::select(height, height_and_expr)
#> # A tibble: 87 x 2
#>    height height_and_expr
#>     <int>           <int>
#>  1    172             174
#>  2    167             169
#>  3     96              98
#>  4    202             204
#>  5    150             152
#>  6    178             180
#>  7    165             167
#>  8     97              99
#>  9    183             185
#> 10    182             184
#> # ... with 77 more rows

#3

Awesome! Just extending this idea a bit further... Is it possible to use tidy eval to build expressions in a dplyr pipeline? I can seem to build expressions by first using paste and then parsing them but cannot seem to build the expressions using tidy eval.

suppressPackageStartupMessages(library(dplyr))
suppressPackageStartupMessages(library(purrr))

# Get the disinct levels of a few of the variables
vars <- starwars %>% 
          summarise_all(n_distinct) %>% 
          tidyr::gather("field", "n_levels") %>% 
          filter(n_levels < 20) %>% 
          mutate(levels = purrr::map(field, ~pull(distinct(select(starwars, .))))) 

vars
#> # A tibble: 3 x 3
#>   field      n_levels levels    
#>   <chr>         <int> <list>    
#> 1 hair_color       13 <chr [13]>
#> 2 eye_color        15 <chr [15]>
#> 3 gender            5 <chr [5]>

# assign some global variables to use in next pipeline
hair_color <- "black"
eye_color <- "black"
gender <- "female"

# for each row, create an expression and evaluate it.
vars2 <- vars %>% 
     mutate(my_expr = map2_chr(field, levels, ~paste0(.x, ' %in% c("', paste(.y[1:3], collapse = '","'), '")'))) %>% 
     mutate(my_expr = map(my_expr, ~rlang::parse_expr(.))) %>% 
     mutate(result = map_lgl(my_expr, ~eval(., envir = .GlobalEnv)))
     
# This works!
vars2$my_expr
#> [[1]]
#> hair_color %in% c("blond", "NA", "none")
#> 
#> [[2]]
#> eye_color %in% c("blue", "yellow", "red")
#> 
#> [[3]]
#> gender %in% c("male", "NA", "female")
vars2$result
#> [1] FALSE FALSE  TRUE


# However to get this to work I had to create my expression as a string and then parse it.
# I can't seem to do the same thing using tidy eval. 
# I want to skip the step of creating a string and then parsing it.

vars %>% 
     mutate(my_expr = map2(field, levels, ~rlang::expr(!!.x %in% !!.y[1:3]))) 
#> Error in quos(...): object '.x' not found
     
vars %>% 
     rowwise() %>% 
     mutate(my_expr = rlang::expr(!!field %in% !!levels[1:3])) 
#> Error in quos(...): object 'field' not found
     
    
# I seems that rlang::expr does not know where to look up the unquoted variables. (???)
# Created on 2018-04-16 by the reprex package (v0.2.0).