skip to Main Content

In the following code I try to create a list tibble column at the right end of mtcars, in which: each member of the list is a tibble with rows of mtcars tibble where vs >= 1 and !is.na(gear).

In purrr::map2(), I am using !!dplyr::sym() to convert the input strings to tibble variables for use in dplyr::filter() and tidyr::drop_na(), but this results in an error of

“object ‘.x’ not found”.

Why is this happening?

I know that if I use dplyr::filter_at(.x, ~ {.x >= 1}) and tidyr::drop_na(all_of(.y)), I can avoid this error. But is there anything wrong if I want to convert the arguments .x and .y from strings to tibble variables and use them in filter() and drop_na()? (I remember they accept unquoted tibble variables)

Thanks for your help and suggestions.

library(tidyverse)
mtcars %>%
  tibble::as_tibble() %>%
  dplyr::mutate(vs2 = purrr::map2("vs", "gear", ~ {
    mtcars %>%
      tibble::as_tibble() %>%
      dplyr::filter(!!dplyr::sym(.x) >= 1) %>%
      tidyr::drop_na(!!dplyr::sym(.y))
  }))
#> Error in is_symbol(x): object '.x' not found

Created on 2020-06-10 by the reprex package (v0.3.0)

My session info:

sessionInfo()
R version 3.6.0 (2019-04-26)
Platform: x86_64-redhat-linux-gnu (64-bit)
Running under: CentOS Linux 7 (Core)

Matrix products: default
BLAS/LAPACK: /usr/lib64/R/lib/libRblas.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8       
 [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
[10] LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] forcats_0.5.0   stringr_1.4.0   dplyr_0.8.3     purrr_0.3.3     readr_1.3.1    
 [6] tidyr_1.0.2     tibble_2.1.3    ggplot2_3.2.1   tidyverse_1.3.0 shiny_1.4.0.2  

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.3       lubridate_1.7.4  lattice_0.20-38  ps_1.3.2         assertthat_0.2.1
 [6] digest_0.6.23    mime_0.8         R6_2.4.1         cellranger_1.1.0 backports_1.1.5 
[11] reprex_0.3.0     evaluate_0.14    httr_1.4.1       pillar_1.4.3     rlang_0.4.6     
[16] lazyeval_0.2.2   readxl_1.3.1     rstudioapi_0.10  miniUI_0.1.1.1   whisker_0.4     
[21] callr_3.4.2      rmarkdown_2.1    munsell_0.5.0    broom_0.5.5      compiler_3.6.0  
[26] httpuv_1.5.3.1   modelr_0.1.6     xfun_0.11        pkgconfig_2.0.3  clipr_0.7.0     
[31] htmltools_0.4.0  tidyselect_1.0.0 fansi_0.4.0      crayon_1.3.4     dbplyr_1.4.2    
[36] withr_2.1.2      later_1.0.0      grid_3.6.0       nlme_3.1-139     jsonlite_1.6    
[41] xtable_1.8-4     gtable_0.3.0     lifecycle_0.1.0  DBI_1.1.0        magrittr_1.5    
[46] scales_1.1.0     cli_2.0.0        stringi_1.4.3    fs_1.3.1         promises_1.1.0  
[51] xml2_1.2.2       vctrs_0.2.4      generics_0.0.2   tools_3.6.0      glue_1.3.1      
[56] hms_0.5.2        processx_3.4.2   fastmap_1.0.1    colorspace_1.4-1 rvest_0.3.5     
[61] knitr_1.26       haven_2.2.0     

2

Answers


  1. It is not possible to use !! rlang::sym in a map call nested inside mutate (it only works within the top level of mutate. You can either write a custom funcion, where you use !! rlang::sym() and call this within map2. Or you could use eval instead of !!.

    Below is an option using a custom function. However, I am not sure how your desired output looks like. Also, using a string of length 1 in a map call, doesn’t make much sense, since we could produce the same outcome without map.

    library(tidyverse)
    
    filter_df <- function(x, y) {
      mtcars %>% 
        tibble::as_tibble() %>%
        dplyr::filter(!! rlang::sym(x) >= 1,
                      !is.na(!! rlang::sym(y)))
    }
    
    mtcars %>%
      tibble::as_tibble() %>%
      mutate(vs2 = map2("vs", "gear", filter_df))
    
    #> # A tibble: 32 x 12
    #>      mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb vs2        
    #>    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <list>     
    #>  1  21       6  160    110  3.9   2.62  16.5     0     1     4     4 <tibble [1~
    #>  2  21       6  160    110  3.9   2.88  17.0     0     1     4     4 <tibble [1~
    #>  3  22.8     4  108     93  3.85  2.32  18.6     1     1     4     1 <tibble [1~
    #>  4  21.4     6  258    110  3.08  3.22  19.4     1     0     3     1 <tibble [1~
    #>  5  18.7     8  360    175  3.15  3.44  17.0     0     0     3     2 <tibble [1~
    #>  6  18.1     6  225    105  2.76  3.46  20.2     1     0     3     1 <tibble [1~
    #>  7  14.3     8  360    245  3.21  3.57  15.8     0     0     3     4 <tibble [1~
    #>  8  24.4     4  147.    62  3.69  3.19  20       1     0     4     2 <tibble [1~
    #>  9  22.8     4  141.    95  3.92  3.15  22.9     1     0     4     2 <tibble [1~
    #> 10  19.2     6  168.   123  3.92  3.44  18.3     1     0     4     4 <tibble [1~
    #> # ... with 22 more rows
    

    Created on 2020-06-10 by the reprex package (v0.3.0)

    Note that you can produce the same outcome by just calling the custom function inside mutate wrapped in list (you might need dplyr 1.0.0 for this functinonality):

    mtcars %>%
      tibble::as_tibble() %>%
      mutate(vs2 = list(filter_df("vs", "gear")))
    

    This would be an alternative using eval and map2:

    library(tidyverse)
    
    mtcars %>%
      tibble::as_tibble() %>%
      mutate(vs2 = map2("vs", "gear",
                        ~ mtcars %>% 
                          tibble::as_tibble() %>%
                          dplyr::filter(eval(rlang::sym(.x)) >= 1,
                                        !is.na(eval(rlang::sym(.y))))
                        )
             )
    #> # A tibble: 32 x 12
    #>      mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb vs2        
    #>    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <list>     
    #>  1  21       6  160    110  3.9   2.62  16.5     0     1     4     4 <tibble [1~
    #>  2  21       6  160    110  3.9   2.88  17.0     0     1     4     4 <tibble [1~
    #>  3  22.8     4  108     93  3.85  2.32  18.6     1     1     4     1 <tibble [1~
    #>  4  21.4     6  258    110  3.08  3.22  19.4     1     0     3     1 <tibble [1~
    #>  5  18.7     8  360    175  3.15  3.44  17.0     0     0     3     2 <tibble [1~
    #>  6  18.1     6  225    105  2.76  3.46  20.2     1     0     3     1 <tibble [1~
    #>  7  14.3     8  360    245  3.21  3.57  15.8     0     0     3     4 <tibble [1~
    #>  8  24.4     4  147.    62  3.69  3.19  20       1     0     4     2 <tibble [1~
    #>  9  22.8     4  141.    95  3.92  3.15  22.9     1     0     4     2 <tibble [1~
    #> 10  19.2     6  168.   123  3.92  3.44  18.3     1     0     4     4 <tibble [1~
    #> # ... with 22 more rows
    

    Created on 2020-06-10 by the reprex package (v0.3.0)

    Add on

    Since the OP shows a very minimal example, here is a more realistic approach where the tibble contains a character column with variable names. In this case map is no longer needed under dplyr >= 1.0.0, since we can use rowwise and mutate.

    library(tidyverse)
    
    filter_df <- function(df, x) {
      df %>% 
        tibble::as_tibble() %>%
        dplyr::filter(!! rlang::sym(x) >= mean(!! rlang::sym(x)))
    }
    
    tibble(data = list(tibble(mtcars)),
           var_names = names(mtcars)) %>% 
      rowwise() %>% 
      mutate(new_data = list(filter_df(data, var_names)))
    
    #> # A tibble: 11 x 3
    #> # Rowwise: 
    #>    data               var_names new_data          
    #>    <list>             <chr>     <list>            
    #>  1 <tibble [32 × 11]> mpg       <tibble [14 × 11]>
    #>  2 <tibble [32 × 11]> cyl       <tibble [14 × 11]>
    #>  3 <tibble [32 × 11]> disp      <tibble [15 × 11]>
    #>  4 <tibble [32 × 11]> hp        <tibble [15 × 11]>
    #>  5 <tibble [32 × 11]> drat      <tibble [18 × 11]>
    #>  6 <tibble [32 × 11]> wt        <tibble [16 × 11]>
    #>  7 <tibble [32 × 11]> qsec      <tibble [15 × 11]>
    #>  8 <tibble [32 × 11]> vs        <tibble [14 × 11]>
    #>  9 <tibble [32 × 11]> am        <tibble [13 × 11]>
    #> 10 <tibble [32 × 11]> gear      <tibble [17 × 11]>
    #> 11 <tibble [32 × 11]> carb      <tibble [15 × 11]>
    

    Created on 2020-06-10 by the reprex package (v0.3.0)

    Login or Signup to reply.
  2. dplyr::sym creates a symbol from a string; you’d have to write dplyr::sym(".x").

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search