r-lib / rlang

Low-level API for programming with R
https://rlang.r-lib.org
Other
504 stars 140 forks source link

mutate with strings in v0.2.0 #431

Closed samuelhuerga closed 6 years ago

samuelhuerga commented 6 years ago

Hi,

I am wondering about how to use strings with new rlang in order to mutate a dataframe. Until now, I've prepared a string with the operation I want to do (and use of := in order properly assign the name of new variable. Then parse_expr, and then mutate with !!.

It also worked with multiple variables, with map(parse_expr) and !!!.

However, now I found that this is no longer allowed.

I would like to know how can I, based on strings, mutate a dataframe with rlang.

library(tidyverse)
#> -- Attaching packages ---------------------------------- tidyverse 1.2.1 --
#> v ggplot2 2.2.1     v purrr   0.2.4
#> v tibble  1.3.4     v dplyr   0.7.4
#> v tidyr   0.7.2     v stringr 1.2.0
#> v readr   1.1.1     v forcats 0.2.0
#> -- Conflicts ------------------------------------- tidyverse_conflicts() --
#> x dplyr::filter() masks stats::filter()
#> x dplyr::lag()    masks stats::lag()
library(rlang)
#> 
#> Attaching package: 'rlang'
#> The following objects are masked from 'package:purrr':
#> 
#>     %@%, %||%, as_function, flatten, flatten_chr, flatten_dbl,
#>     flatten_int, flatten_lgl, invoke, list_along, modify, prepend,
#>     rep_along, splice
#> The following object is masked from 'package:tibble':
#> 
#>     has_name

df <- tribble(
  ~x , ~y,
  1 ,  2,
  3 ,  4
)

f <- "z := y*2"
f_q <- parse_expr(f)
df %>% mutate(!!f_q)
#> Error in mutate_impl(.data, dots): Evaluation error: `:=` can only be used within a quasiquoted argument.

With version 0.1.6:

library(tidyverse)
#> -- Attaching packages ---------------------------------- tidyverse 1.2.1 --
#> v ggplot2 2.2.1     v purrr   0.2.4
#> v tibble  1.3.4     v dplyr   0.7.4
#> v tidyr   0.7.2     v stringr 1.2.0
#> v readr   1.1.1     v forcats 0.2.0
#> -- Conflicts ------------------------------------- tidyverse_conflicts() --
#> x dplyr::filter() masks stats::filter()
#> x dplyr::lag()    masks stats::lag()
library(rlang)
#> 
#> Attaching package: 'rlang'
#> The following objects are masked from 'package:purrr':
#> 
#>     %@%, %||%, as_function, flatten, flatten_chr, flatten_dbl,
#>     flatten_int, flatten_lgl, invoke, list_along, modify, prepend,
#>     rep_along, splice
#> The following object is masked from 'package:tibble':
#> 
#>     has_name

df <- tribble(
  ~x , ~y,
  1 ,  2,
  3 ,  4
)

f <- "n2 := y*2"
f_q <- parse_expr(f)
df %>% mutate(!!f_q)
#> # A tibble: 2 x 3
#>       x     y    n2
#>   <dbl> <dbl> <dbl>
#> 1     1     2     4
#> 2     3     4     8

Thanks!

lionel- commented 6 years ago

This is not possible by construction.

lionel- commented 6 years ago

well I guess you could do it with eval_tidy(quo(mutate(df, !!f_q))) but this is convoluted.

lionel- commented 6 years ago

Maybe you'll have better luck unquote-splicing a named list with !!! if you don't know in advance which arguments are named? You can !!! a list that has named and/or unnamed elements (even if this list only has one element).

samuelhuerga commented 6 years ago

Thanks Lionel,

The list approach reminds me of mutate_ of dplyr 0.5, where I started mutating df based on strings.

I think I'll encapsulate your options in a function in order to reuse and change as little as possible in all my code.

Thanks!