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
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
4 Likes
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).