4

We can define a function that uses a dynamic column name, then use it in map. Note how the tibbles from ListCol have the column names a and b, taken from the Letter column in the original tibble.

library(tidyverse)

myfunc <- function(col_name) {
  tibble(!!sym(col_name) := c(1, 2))
}

tibble(
  Letter = letters[1:2],
  ListCol = map(Letter, myfunc)
) %>%
  pull(ListCol)
#> [[1]]
#> # A tibble: 2 x 1
#>       a
#>   <dbl>
#> 1     1
#> 2     2
#> 
#> [[2]]
#> # A tibble: 2 x 1
#>       b
#>   <dbl>
#> 1     1
#> 2     2

However, using the same code from the previous myfunc function in an anonymous function gives an error.

tibble(
  Letter = letters[1:2],
  ListCol = map(Letter, function(col_name) {
    tibble(!!sym(col_name) := c(1, 2))
  })
) %>%
  pull(ListCol)
#> Error in is_symbol(x): object 'col_name' not found

Created on 2020-01-12 by the reprex package (v0.3.0)

devtools::session_info()
#> ─ Session info ──────────────────────────────────────────────────────────
#>  setting  value                       
#>  version  R version 3.6.1 (2019-07-05)
#>  os       macOS Sierra 10.12.6        
#>  system   x86_64, darwin15.6.0        
#>  ui       X11                         
#>  language (EN)                        
#>  collate  en_GB.UTF-8                 
#>  ctype    en_GB.UTF-8                 
#>  tz       Europe/London               
#>  date     2020-01-12                  
#> 
#> ─ Packages ──────────────────────────────────────────────────────────────
#>  package     * version date       lib source        
#>  assertthat    0.2.1   2019-03-21 [1] CRAN (R 3.6.0)
#>  backports     1.1.5   2019-10-02 [1] CRAN (R 3.6.0)
#>  broom         0.5.2   2019-04-07 [1] CRAN (R 3.6.0)
#>  callr         3.3.2   2019-09-22 [1] CRAN (R 3.6.1)
#>  cellranger    1.1.0   2016-07-27 [1] CRAN (R 3.6.0)
#>  cli           2.0.0   2019-12-09 [1] CRAN (R 3.6.0)
#>  colorspace    1.4-1   2019-03-18 [1] CRAN (R 3.6.0)
#>  crayon        1.3.4   2017-09-16 [1] CRAN (R 3.6.0)
#>  DBI           1.0.0   2018-05-02 [1] CRAN (R 3.6.0)
#>  dbplyr        1.4.2   2019-06-17 [1] CRAN (R 3.6.0)
#>  desc          1.2.0   2018-05-01 [1] CRAN (R 3.6.0)
#>  devtools      2.2.0   2019-09-07 [1] CRAN (R 3.6.0)
#>  digest        0.6.23  2019-11-23 [1] CRAN (R 3.6.1)
#>  dplyr       * 0.8.3   2019-07-04 [1] CRAN (R 3.6.0)
#>  DT            0.9     2019-09-17 [1] CRAN (R 3.6.0)
#>  ellipsis      0.3.0   2019-09-20 [1] CRAN (R 3.6.0)
#>  evaluate      0.14    2019-05-28 [1] CRAN (R 3.6.0)
#>  fansi         0.4.0   2018-10-05 [1] CRAN (R 3.6.0)
#>  forcats     * 0.4.0   2019-02-17 [1] CRAN (R 3.6.0)
#>  fs            1.3.1   2019-05-06 [1] CRAN (R 3.6.0)
#>  generics      0.0.2   2018-11-29 [1] CRAN (R 3.6.0)
#>  ggplot2     * 3.2.1   2019-08-10 [1] CRAN (R 3.6.0)
#>  glue          1.3.1   2019-03-12 [1] CRAN (R 3.6.0)
#>  gtable        0.3.0   2019-03-25 [1] CRAN (R 3.6.0)
#>  haven         2.2.0   2019-11-08 [1] CRAN (R 3.6.0)
#>  highr         0.8     2019-03-20 [1] CRAN (R 3.6.0)
#>  hms           0.5.3   2020-01-08 [1] CRAN (R 3.6.0)
#>  htmltools     0.4.0   2019-10-04 [1] CRAN (R 3.6.0)
#>  htmlwidgets   1.3     2018-09-30 [1] CRAN (R 3.6.0)
#>  httr          1.4.1   2019-08-05 [1] CRAN (R 3.6.1)
#>  jsonlite      1.6     2018-12-07 [1] CRAN (R 3.6.0)
#>  knitr         1.25    2019-09-18 [1] CRAN (R 3.6.0)
#>  lattice       0.20-38 2018-11-04 [1] CRAN (R 3.6.1)
#>  lazyeval      0.2.2   2019-03-15 [1] CRAN (R 3.6.0)
#>  lifecycle     0.1.0   2019-08-01 [1] CRAN (R 3.6.0)
#>  lubridate     1.7.4   2018-04-11 [1] CRAN (R 3.6.0)
#>  magrittr      1.5     2014-11-22 [1] CRAN (R 3.6.0)
#>  memoise       1.1.0   2017-04-21 [1] CRAN (R 3.6.0)
#>  modelr        0.1.5   2019-08-08 [1] CRAN (R 3.6.0)
#>  munsell       0.5.0   2018-06-12 [1] CRAN (R 3.6.0)
#>  nlme          3.1-141 2019-08-01 [1] CRAN (R 3.6.0)
#>  pillar        1.4.3   2019-12-20 [1] CRAN (R 3.6.0)
#>  pkgbuild      1.0.5   2019-08-26 [1] CRAN (R 3.6.0)
#>  pkgconfig     2.0.3   2019-09-22 [1] CRAN (R 3.6.1)
#>  pkgload       1.0.2   2018-10-29 [1] CRAN (R 3.6.0)
#>  prettyunits   1.0.2   2015-07-13 [1] CRAN (R 3.6.0)
#>  processx      3.4.1   2019-07-18 [1] CRAN (R 3.6.0)
#>  ps            1.3.0   2018-12-21 [1] CRAN (R 3.6.0)
#>  purrr       * 0.3.3   2019-10-18 [1] CRAN (R 3.6.0)
#>  R6            2.4.1   2019-11-12 [1] CRAN (R 3.6.0)
#>  Rcpp          1.0.3   2019-11-08 [1] CRAN (R 3.6.0)
#>  readr       * 1.3.1   2018-12-21 [1] CRAN (R 3.6.0)
#>  readxl        1.3.1   2019-03-13 [1] CRAN (R 3.6.0)
#>  remotes       2.1.0   2019-06-24 [1] CRAN (R 3.6.0)
#>  reprex        0.3.0   2019-05-16 [1] CRAN (R 3.6.0)
#>  rlang         0.4.2   2019-11-23 [1] CRAN (R 3.6.1)
#>  rmarkdown     1.15    2019-08-21 [1] CRAN (R 3.6.0)
#>  rprojroot     1.3-2   2018-01-03 [1] CRAN (R 3.6.0)
#>  rvest         0.3.5   2019-11-08 [1] CRAN (R 3.6.0)
#>  scales        1.1.0   2019-11-18 [1] CRAN (R 3.6.0)
#>  sessioninfo   1.1.1   2018-11-05 [1] CRAN (R 3.6.0)
#>  stringi       1.4.3   2019-03-12 [1] CRAN (R 3.6.0)
#>  stringr     * 1.4.0   2019-02-10 [1] CRAN (R 3.6.0)
#>  testthat      2.2.1   2019-07-25 [1] CRAN (R 3.6.0)
#>  tibble      * 2.1.3   2019-06-06 [1] CRAN (R 3.6.0)
#>  tidyr       * 1.0.0   2019-09-11 [1] CRAN (R 3.6.0)
#>  tidyselect    0.2.5   2018-10-11 [1] CRAN (R 3.6.0)
#>  tidyverse   * 1.3.0   2019-11-21 [1] CRAN (R 3.6.0)
#>  usethis       1.5.1   2019-07-04 [1] CRAN (R 3.6.0)
#>  utf8          1.1.4   2018-05-24 [1] CRAN (R 3.6.0)
#>  vctrs         0.2.1   2019-12-17 [1] CRAN (R 3.6.0)
#>  withr         2.1.2   2018-03-15 [1] CRAN (R 3.6.0)
#>  xfun          0.9     2019-08-21 [1] CRAN (R 3.6.0)
#>  xml2          1.2.2   2019-08-09 [1] CRAN (R 3.6.0)
#>  yaml          2.2.0   2018-07-25 [1] CRAN (R 3.6.0)
#>  zeallot       0.1.0   2018-01-28 [1] CRAN (R 3.6.0)
#> 
#> [1] /Library/Frameworks/R.framework/Versions/3.6/Resources/library
Greg
  • 487
  • 5
  • 15
  • Not sure what is happening there. It works separately though, `df <- tibble(Letter = letters[1:2])` and `map(df$Letter, ~tibble(!!sym(.x) := c(1, 2)))` – Ronak Shah Jan 12 '20 at 12:58
  • I think this post is referring to the same point https://stackoverflow.com/questions/51902438/rlangsym-in-anonymous-functions/ – Ronak Shah Jan 12 '20 at 13:17
  • Yes, [this comment](https://stackoverflow.com/questions/51902438/rlangsym-in-anonymous-functions/#comment90764125_51902438) from Lionel Henry seems to explain the issue – Greg Jan 12 '20 at 13:23
  • do you think it is safe to mark this post as a duplicate of that one? Does it answer your question? – Ronak Shah Jan 12 '20 at 13:25
  • I'm not sure whether it's a duplicate because in the other question, `!!` is being used on the right-hand side of a `mutate` rather than the left-hand side. Unfortunately if you try using `eval`, you get `Error: The LHS of := must be a string or a symbol` – Greg Jan 12 '20 at 13:28

2 Answers2

0

Thanks to Ronak Shah for finding this related question in which Lionel Henry explains the issue:

Unquoting is not a function call: it always takes effect at the very first, outermost, quoting function. That's why you have to be a bit careful with anonymous functions. Unquoting happens immediately while anonymous functions denote a scope that is created later on, so there's a timing problem.

This is also linked from Lionel and Hadley Wickham's online book on tidyeval.

Unfortunately the answer to my original question seems to be that it's not possible – you need to create a named function and call this as in the first example.

Unlike in the related question, you cannot use eval(sym(col_name)) to fix the issue.

Greg
  • 487
  • 5
  • 15
  • I think there might be alternative `map_*` functions that negate the need for tidy eval and/or an anon. – NelsonGon Jan 12 '20 at 13:33
  • I think these are for returning different types of object e.g. `map_chr` returns a character vector. Here we want to return a tibble so we use `map` – Greg Jan 12 '20 at 13:35
  • Yes but I think you can still get your result without using an anonymous function, maybe by using "canonical" `~` syntax. – NelsonGon Jan 12 '20 at 13:38
  • 1
    Unfortunately replacing the anonymous function with `~ tibble(!!(sym(col_name)) := c(1, 2))` gives the same error: `Error in is_symbol(x) : object 'col_name' not found` – Greg Jan 12 '20 at 13:40
  • We plan to fix this in rlang 0.5 by preventing `!!` from crossing `~` and `function` calls (unless qq is explicit like with `expr()`). This will be a breaking change but will help a lot in cases like these. See https://github.com/r-lib/rlang/issues/845. – Lionel Henry Jan 13 '20 at 08:55
0

You have a very good handle on the source of issue. I just wanted to point out that one alternative solution is to rewrite your expression in a way that doesn't use !! unquoting for variables that are internal to the anonymous function (and thus not initialized before the unquoting takes place):

tibble(
  Letter = letters[1:2],
  ListCol = map(Letter, function(col_name) {
      do.call(tibble, set_names(list(c(1,2)), col_name))
  })
) %>%
  pull(ListCol)
Artem Sokolov
  • 13,196
  • 4
  • 43
  • 74