dplyr icon indicating copy to clipboard operation
dplyr copied to clipboard

How to adjust to different behavior of across .fn argument passing in dplyr 1.1.1 vs. 1.0.10?

Open eipi10 opened this issue 1 year ago • 14 comments

I have a summarizing function that's similar to the function below. It allows the user to pass grouping variables, summary variables and any number of summary functions as arguments.

# Allow user to choose summary function(s)
fnc = function(data, summary.vars=NULL, group.vars=NULL, 
               FUNS=c(mean=~mean(., na.rm=TRUE))) {
  data %>% 
    group_by(across({{group.vars}})) %>% 
    summarise(n=n(), across({{summary.vars}}, 
                            .fn=FUNS,
                            .names="{.col}_{.fn}"))
}

I often use weighted.mean as a summary function in the FUNS argument, which requires a weighting variable, which I pass with the bare column name, like this:

mtcars %>% fnc(c(mpg, hp), c(vs, am), 
               FUNS=c(mean=~mean(., na.rm=TRUE), 
                      mean.wted=~weighted.mean(., w=cyl, na.rm=TRUE)))

This approach worked in dplyr 1.0.10 and previous versions, but is failing in dplyr 1.1.1. Reproducible examples are below, first with 1.0.10 then with 1.1.1.

How can I update my function so that it will work properly with dplyr 1.1.1? I've never been happy with hard-coding the w argument anyway. Is there some tidyeval way that I should be passing the w argument into the summary function?

Example with dplyr 1.0.10

library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union

# Allow user to choose summary function(s)
fnc = function(data, summary.vars=NULL, group.vars=NULL, 
               FUNS=c(mean=~mean(., na.rm=TRUE))) {
  data %>% 
    group_by(across({{group.vars}})) %>% 
    summarise(n=n(), across({{summary.vars}}, 
                            .fn=FUNS,
                            .names="{.col}_{.fn}"))
}

# These work in both 1.0.10 and 1.1.1
mtcars %>% fnc(c(mpg, hp))
#> # A tibble: 1 × 3
#>       n mpg_mean hp_mean
#>   <int>    <dbl>   <dbl>
#> 1    32     20.1    147.

mtcars %>% fnc(c(mpg, hp), c(vs, am))
#> `summarise()` has grouped output by 'vs'. You can override using the `.groups`
#> argument.
#> # A tibble: 4 × 5
#> # Groups:   vs [2]
#>      vs    am     n mpg_mean hp_mean
#>   <dbl> <dbl> <int>    <dbl>   <dbl>
#> 1     0     0    12     15.0   194. 
#> 2     0     1     6     19.8   181. 
#> 3     1     0     7     20.7   102. 
#> 4     1     1     7     28.4    80.6

# Passing a weighting variable as the w argument in weighted.mean works in dplyr 1.0.10 but fails in dplyr 1.1.1
mtcars %>% fnc(c(mpg, hp), c(vs, am), 
               FUNS=c(mean=~mean(., na.rm=TRUE), 
                      mean.wted=~weighted.mean(., w=cyl, na.rm=TRUE)))
#> `summarise()` has grouped output by 'vs'. You can override using the `.groups`
#> argument.
#> # A tibble: 4 × 7
#> # Groups:   vs [2]
#>      vs    am     n mpg_mean mpg_mean.wted hp_mean hp_mean.wted
#>   <dbl> <dbl> <int>    <dbl>         <dbl>   <dbl>        <dbl>
#> 1     0     0    12     15.0          15.1   194.         194. 
#> 2     0     1     6     19.8          19.0   181.         198. 
#> 3     1     0     7     20.7          20.4   102.         105. 
#> 4     1     1     7     28.4          28.4    80.6         80.6

sessionInfo()
#> R version 4.2.2 (2022-10-31)
#> Platform: aarch64-apple-darwin20 (64-bit)
#> Running under: macOS Ventura 13.2.1
#> 
#> Matrix products: default
#> BLAS:   /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/lib/libRblas.0.dylib
#> LAPACK: /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/lib/libRlapack.dylib
#> 
#> locale:
#> [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] dplyr_1.0.10
#> 
#> loaded via a namespace (and not attached):
#>  [1] rstudioapi_0.14   knitr_1.42        magrittr_2.0.3    tidyselect_1.2.0 
#>  [5] R.cache_0.16.0    R6_2.5.1          rlang_1.1.0.9000  fastmap_1.1.1    
#>  [9] fansi_1.0.4       styler_1.9.1      tools_4.2.2       xfun_0.37        
#> [13] R.oo_1.25.0       utf8_1.2.3        DBI_1.1.3         cli_3.6.1        
#> [17] withr_2.5.0       htmltools_0.5.4   yaml_2.3.7        digest_0.6.31    
#> [21] tibble_3.2.1      lifecycle_1.0.3   purrr_1.0.1       vctrs_0.6.0.9000 
#> [25] R.utils_2.12.2    fs_1.6.1          glue_1.6.2        evaluate_0.20    
#> [29] rmarkdown_2.20    reprex_2.0.2      compiler_4.2.2    pillar_1.8.1     
#> [33] generics_0.1.3    R.methodsS3_1.8.2 pkgconfig_2.0.3

Same example, but with dplyr 1.1.1

library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union

# Allow user to choose summary function(s)
fnc = function(data, summary.vars=NULL, group.vars=NULL, 
               FUNS=c(mean=~mean(., na.rm=TRUE))) {
  data %>% 
    group_by(across({{group.vars}})) %>% 
    summarise(n=n(), across({{summary.vars}}, 
                            .fn=FUNS,
                            .names="{.col}_{.fn}"))
}

# These work in both 1.0.10 and 1.1.1
mtcars %>% fnc(c(mpg, hp))
#> # A tibble: 1 × 3
#>       n mpg_mean hp_mean
#>   <int>    <dbl>   <dbl>
#> 1    32     20.1    147.

mtcars %>% fnc(c(mpg, hp), c(vs, am))
#> `summarise()` has grouped output by 'vs'. You can override using the `.groups`
#> argument.
#> # A tibble: 4 × 5
#> # Groups:   vs [2]
#>      vs    am     n mpg_mean hp_mean
#>   <dbl> <dbl> <int>    <dbl>   <dbl>
#> 1     0     0    12     15.0   194. 
#> 2     0     1     6     19.8   181. 
#> 3     1     0     7     20.7   102. 
#> 4     1     1     7     28.4    80.6

# Passing a weighting variable as the w argument in weighted.mean works in dplyr 1.0.10 but fails in dplyr 1.1.1
mtcars %>% fnc(c(mpg, hp), c(vs, am), 
               FUNS=c(mean=~mean(., na.rm=TRUE), 
                      mean.wted=~weighted.mean(., w=cyl, na.rm=TRUE)))
#> Error in `summarise()`:
#> ℹ In argument: `across(c(mpg, hp), .fn = FUNS, .names =
#>   "{.col}_{.fn}")`.
#> ℹ In group 1: `vs = 0`, `am = 0`.
#> Caused by error in `across()`:
#> ! Can't compute column `mpg_mean.wted`.
#> Caused by error in `weighted.mean.default()`:
#> ! object 'cyl' not found
#> Backtrace:
#>      ▆
#>   1. ├─mtcars %>% ...
#>   2. ├─global fnc(...)
#>   3. │ └─data %>% group_by(across({{ group.vars }})) %>% ...
#>   4. ├─dplyr::summarise(...)
#>   5. ├─dplyr:::summarise.grouped_df(...)
#>   6. │ └─dplyr:::summarise_cols(.data, dplyr_quosures(...), by, "summarise")
#>   7. │   ├─base::withCallingHandlers(...)
#>   8. │   └─dplyr:::map(quosures, summarise_eval_one, mask = mask)
#>   9. │     └─base::lapply(.x, .f, ...)
#>  10. │       └─dplyr (local) FUN(X[[i]], ...)
#>  11. │         ├─base::withCallingHandlers(...)
#>  12. │         └─mask$eval_all_summarise(quo)
#>  13. │           └─dplyr (local) eval()
#>  14. ├─`<rlng_lm_>`(mpg)
#>  15. │ ├─stats::weighted.mean(., w = cyl, na.rm = TRUE)
#>  16. │ └─stats:::weighted.mean.default(., w = cyl, na.rm = TRUE)
#>  17. └─base::.handleSimpleError(...)
#>  18.   └─dplyr (local) h(simpleError(msg, call))
#>  19.     └─rlang::abort(msg, call = call("across"), parent = cnd)

sessionInfo()
#> R version 4.2.2 (2022-10-31)
#> Platform: aarch64-apple-darwin20 (64-bit)
#> Running under: macOS Ventura 13.2.1
#> 
#> Matrix products: default
#> BLAS:   /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/lib/libRblas.0.dylib
#> LAPACK: /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/lib/libRlapack.dylib
#> 
#> locale:
#> [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] dplyr_1.1.1
#> 
#> loaded via a namespace (and not attached):
#>  [1] rstudioapi_0.14   knitr_1.42        magrittr_2.0.3    tidyselect_1.2.0 
#>  [5] R.cache_0.16.0    R6_2.5.1          rlang_1.1.0.9000  fastmap_1.1.1    
#>  [9] fansi_1.0.4       styler_1.9.1      tools_4.2.2       xfun_0.37        
#> [13] R.oo_1.25.0       utf8_1.2.3        cli_3.6.0         withr_2.5.0      
#> [17] htmltools_0.5.4   yaml_2.3.7        digest_0.6.31     tibble_3.2.1     
#> [21] lifecycle_1.0.3   purrr_1.0.1       vctrs_0.6.0.9000  R.utils_2.12.2   
#> [25] fs_1.6.1          glue_1.6.2        evaluate_0.20     rmarkdown_2.20   
#> [29] reprex_2.0.2      compiler_4.2.2    pillar_1.8.1      generics_0.1.3   
#> [33] R.methodsS3_1.8.2 pkgconfig_2.0.3

eipi10 avatar Mar 27 '23 18:03 eipi10

Minimal reprex

1.0.10:

# pak::pak("tidyverse/[email protected]")
library(dplyr, warn.conflicts = FALSE)

df <- tibble(x = 1:5, w = 2:6)

fn <- function(data, cols, fns) {
  summarise(data, across(.cols = {{cols}}, .fns = fns))
}

# Works from top level
summarise(df, across(x, ~weighted.mean(.x, w = w)))
#> # A tibble: 1 × 1
#>       x
#>   <dbl>
#> 1   3.5

# Works when wrapped
fn(df, x, ~weighted.mean(.x, w = w))
#> # A tibble: 1 × 1
#>       x
#>   <dbl>
#> 1   3.5

1.1.1:

library(dplyr, warn.conflicts = FALSE)

df <- tibble(x = 1:5, w = 2:6)

fn <- function(data, cols, fns) {
  summarise(data, across(.cols = {{cols}}, .fns = fns))
}

# Works from top level
summarise(df, across(x, ~weighted.mean(.x, w = w)))
#> # A tibble: 1 × 1
#>       x
#>   <dbl>
#> 1   3.5

# Not when wrapped
fn(df, x, ~weighted.mean(.x, w = w))
#> Error in `summarise()`:
#> ℹ In argument: `across(.cols = x, .fns = fns)`.
#> Caused by error in `across()`:
#> ! Can't compute column `x`.
#> Caused by error in `weighted.mean.default()`:
#> ! object 'w' not found

DavisVaughan avatar Mar 31 '23 15:03 DavisVaughan

Possible solution proposed by @lionel- is to allow .fns = {{ fns }} so that users can wrap with a pattern like:

fn <- function(data, cols, fns) {
  summarise(data, across(.cols = {{cols}}, .fns = {{fns}}))
}

The justification here being that if across() is a true templating function then it needs to be able to access the original expressions for .fns, so they need to come through with {{

DavisVaughan avatar Mar 31 '23 16:03 DavisVaughan

Actually, that already works (assuming I'm understanding what you and @lionel- had in mind):

library(tidyverse)

fn <- function(data, cols, fns, groups=NULL) {
  data %>% 
    group_by(across({{groups}})) %>% 
    summarise(across(.cols = {{cols}}, .fns = {{fns}}))
}

d = tibble(
  x1=1:5, x2=11:15, w=2:6, g=rep(LETTERS[1:2], c(2,3))
) 

fn(d, 
   cols=c(x1,x2), 
   fns=c(mean=mean, mean.wt=~weighted.mean(., w=w)), 
   groups=g)
#> # A tibble: 2 × 5
#>   g     x1_mean x1_mean.wt x2_mean x2_mean.wt
#>   <chr>   <dbl>      <dbl>   <dbl>      <dbl>
#> 1 A         1.5       1.6     11.5       11.6
#> 2 B         4         4.13    14         14.1

fn(mtcars, 
   cols=c(mpg, hp), 
   fns=c(mean=mean, mean.wt=~weighted.mean(., w=cyl)), 
   groups=c(am, vs))
#> `summarise()` has grouped output by 'am'. You can override using the `.groups`
#> argument.
#> # A tibble: 4 × 6
#> # Groups:   am [2]
#>      am    vs mpg_mean mpg_mean.wt hp_mean hp_mean.wt
#>   <dbl> <dbl>    <dbl>       <dbl>   <dbl>      <dbl>
#> 1     0     0     15.0        15.1   194.       194. 
#> 2     0     1     20.7        20.4   102.       105. 
#> 3     1     0     19.8        19.0   181.       198. 
#> 4     1     1     28.4        28.4    80.6       80.6

Created on 2023-03-31 with reprex v2.0.2

Session info
sessioninfo::session_info()
#> ─ Session info ───────────────────────────────────────────────────────────────
#>  setting  value
#>  version  R version 4.2.2 (2022-10-31)
#>  os       macOS Ventura 13.2.1
#>  system   aarch64, darwin20
#>  ui       X11
#>  language (EN)
#>  collate  en_US.UTF-8
#>  ctype    en_US.UTF-8
#>  tz       America/Los_Angeles
#>  date     2023-03-31
#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/ (via rmarkdown)
#> 
#> ─ Packages ───────────────────────────────────────────────────────────────────
#>  package     * version    date (UTC) lib source
#>  cli           3.6.1      2023-03-23 [1] CRAN (R 4.2.0)
#>  colorspace    2.1-0      2023-01-23 [1] CRAN (R 4.2.0)
#>  digest        0.6.31     2022-12-11 [1] CRAN (R 4.2.0)
#>  dplyr       * 1.1.1      2023-03-22 [1] CRAN (R 4.2.0)
#>  evaluate      0.20       2023-01-17 [1] CRAN (R 4.2.0)
#>  fansi         1.0.4      2023-01-22 [1] CRAN (R 4.2.0)
#>  fastmap       1.1.1      2023-02-24 [1] CRAN (R 4.2.0)
#>  forcats     * 1.0.0      2023-01-29 [1] CRAN (R 4.2.0)
#>  fs            1.6.1      2023-02-06 [1] CRAN (R 4.2.0)
#>  generics      0.1.3      2022-07-05 [1] CRAN (R 4.2.0)
#>  ggplot2     * 3.4.1      2023-02-10 [1] CRAN (R 4.2.0)
#>  glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
#>  gtable        0.3.3      2023-03-21 [1] CRAN (R 4.2.0)
#>  hms           1.1.3      2023-03-21 [1] CRAN (R 4.2.0)
#>  htmltools     0.5.5      2023-03-23 [1] CRAN (R 4.2.0)
#>  knitr         1.42       2023-01-25 [1] CRAN (R 4.2.0)
#>  lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.0)
#>  lubridate   * 1.9.2      2023-02-10 [1] CRAN (R 4.2.0)
#>  magrittr      2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
#>  munsell       0.5.0      2018-06-12 [1] CRAN (R 4.2.0)
#>  pillar        1.9.0      2023-03-22 [1] CRAN (R 4.2.0)
#>  pkgconfig     2.0.3      2019-09-22 [1] CRAN (R 4.2.0)
#>  purrr       * 1.0.1      2023-01-10 [1] CRAN (R 4.2.0)
#>  R.cache       0.16.0     2022-07-21 [1] CRAN (R 4.2.0)
#>  R.methodsS3   1.8.2      2022-06-13 [1] CRAN (R 4.2.0)
#>  R.oo          1.25.0     2022-06-12 [1] CRAN (R 4.2.0)
#>  R.utils       2.12.2     2022-11-11 [1] CRAN (R 4.2.0)
#>  R6            2.5.1      2021-08-19 [1] CRAN (R 4.2.0)
#>  readr       * 2.1.4      2023-02-10 [1] CRAN (R 4.2.0)
#>  reprex        2.0.2      2022-08-17 [1] CRAN (R 4.2.0)
#>  rlang         1.1.0.9000 2023-03-21 [1] Github (r-lib/rlang@ea2fe5f)
#>  rmarkdown     2.21       2023-03-26 [1] CRAN (R 4.2.2)
#>  rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.0)
#>  scales        1.2.1      2022-08-20 [1] CRAN (R 4.2.0)
#>  sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
#>  stringi       1.7.12     2023-01-11 [1] CRAN (R 4.2.0)
#>  stringr     * 1.5.0      2022-12-02 [1] CRAN (R 4.2.0)
#>  styler        1.9.1      2023-03-04 [1] CRAN (R 4.2.0)
#>  tibble      * 3.2.1      2023-03-20 [1] CRAN (R 4.2.0)
#>  tidyr       * 1.3.0      2023-01-24 [1] CRAN (R 4.2.0)
#>  tidyselect    1.2.0      2022-10-10 [1] CRAN (R 4.2.0)
#>  tidyverse   * 2.0.0      2023-02-22 [1] CRAN (R 4.2.0)
#>  timechange    0.2.0      2023-01-11 [1] CRAN (R 4.2.2)
#>  tzdb          0.3.0      2022-03-28 [1] CRAN (R 4.2.0)
#>  utf8          1.2.3      2023-01-31 [1] CRAN (R 4.2.0)
#>  vctrs         0.6.1      2023-03-22 [1] CRAN (R 4.2.0)
#>  withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
#>  xfun          0.38       2023-03-24 [1] CRAN (R 4.2.0)
#>  yaml          2.3.7      2023-01-23 [1] CRAN (R 4.2.0)
#> 
#>  [1] /Users/***/Library/R/arm64/4.2/library
#>  [2] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
#> 
#> ──────────────────────────────────────────────────────────────────────────────

eipi10 avatar Mar 31 '23 17:03 eipi10

Oh, but it doesn't work if you pass a separate object as the fns argument:

library(tidyverse)

fn <- function(data, cols, fns, groups=NULL) {
  data %>% 
    group_by(across({{groups}})) %>% 
    summarise(across(.cols = {{cols}}, .fns = {{fns}}))
}

fn(mtcars, 
   cols=c(mpg, hp), 
   fns=c(mean=mean, mean.wt=~weighted.mean(., w=cyl)),
   groups=c(am, vs))
#> `summarise()` has grouped output by 'am'. You can override using the `.groups`
#> argument.
#> # A tibble: 4 × 6
#> # Groups:   am [2]
#>      am    vs mpg_mean mpg_mean.wt hp_mean hp_mean.wt
#>   <dbl> <dbl>    <dbl>       <dbl>   <dbl>      <dbl>
#> 1     0     0     15.0        15.1   194.       194. 
#> 2     0     1     20.7        20.4   102.       105. 
#> 3     1     0     19.8        19.0   181.       198. 
#> 4     1     1     28.4        28.4    80.6       80.6

FUNS = c(mean=mean, mean.wt=~weighted.mean(., w=cyl))

fn(mtcars, 
   cols=c(mpg, hp), 
   fns=FUNS, 
   groups=c(am, vs))
#> Error in `summarise()`:
#> ℹ In argument: `across(.cols = c(mpg, hp), .fns = FUNS)`.
#> ℹ In group 1: `am = 0`, `vs = 0`.
#> Caused by error in `across()`:
#> ! Can't compute column `mpg_mean.wt`.
#> Caused by error in `weighted.mean.default()`:
#> ! object 'cyl' not found
#> Backtrace:
#>      ▆
#>   1. ├─global fn(mtcars, cols = c(mpg, hp), fns = FUNS, groups = c(am, vs))
#>   2. │ └─data %>% group_by(across({{ groups }})) %>% ...
#>   3. ├─dplyr::summarise(...)
#>   4. ├─dplyr:::summarise.grouped_df(...)
#>   5. │ └─dplyr:::summarise_cols(.data, dplyr_quosures(...), by, "summarise")
#>   6. │   ├─base::withCallingHandlers(...)
#>   7. │   └─dplyr:::map(quosures, summarise_eval_one, mask = mask)
#>   8. │     └─base::lapply(.x, .f, ...)
#>   9. │       └─dplyr (local) FUN(X[[i]], ...)
#>  10. │         ├─base::withCallingHandlers(...)
#>  11. │         └─mask$eval_all_summarise(quo)
#>  12. │           └─dplyr (local) eval()
#>  13. ├─global `<rlng_lm_>`(mpg)
#>  14. │ ├─stats::weighted.mean(., w = cyl)
#>  15. │ └─stats:::weighted.mean.default(., w = cyl)
#>  16. └─base::.handleSimpleError(...)
#>  17.   └─dplyr (local) h(simpleError(msg, call))
#>  18.     └─rlang::abort(msg, call = call("across"), parent = cnd)

Created on 2023-04-01 with reprex v2.0.2

Session info
sessioninfo::session_info()
#> ─ Session info ───────────────────────────────────────────────────────────────
#>  setting  value
#>  version  R version 4.2.2 (2022-10-31)
#>  os       macOS Ventura 13.2.1
#>  system   aarch64, darwin20
#>  ui       X11
#>  language (EN)
#>  collate  en_US.UTF-8
#>  ctype    en_US.UTF-8
#>  tz       America/Los_Angeles
#>  date     2023-04-01
#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/ (via rmarkdown)
#> 
#> ─ Packages ───────────────────────────────────────────────────────────────────
#>  package     * version    date (UTC) lib source
#>  cli           3.6.1      2023-03-23 [1] CRAN (R 4.2.0)
#>  colorspace    2.1-0      2023-01-23 [1] CRAN (R 4.2.0)
#>  digest        0.6.31     2022-12-11 [1] CRAN (R 4.2.0)
#>  dplyr       * 1.1.1      2023-03-22 [1] CRAN (R 4.2.0)
#>  evaluate      0.20       2023-01-17 [1] CRAN (R 4.2.0)
#>  fansi         1.0.4      2023-01-22 [1] CRAN (R 4.2.0)
#>  fastmap       1.1.1      2023-02-24 [1] CRAN (R 4.2.0)
#>  forcats     * 1.0.0      2023-01-29 [1] CRAN (R 4.2.0)
#>  fs            1.6.1      2023-02-06 [1] CRAN (R 4.2.0)
#>  generics      0.1.3      2022-07-05 [1] CRAN (R 4.2.0)
#>  ggplot2     * 3.4.1      2023-02-10 [1] CRAN (R 4.2.0)
#>  glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
#>  gtable        0.3.3      2023-03-21 [1] CRAN (R 4.2.0)
#>  hms           1.1.3      2023-03-21 [1] CRAN (R 4.2.0)
#>  htmltools     0.5.5      2023-03-23 [1] CRAN (R 4.2.0)
#>  knitr         1.42       2023-01-25 [1] CRAN (R 4.2.0)
#>  lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.0)
#>  lubridate   * 1.9.2      2023-02-10 [1] CRAN (R 4.2.0)
#>  magrittr      2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
#>  munsell       0.5.0      2018-06-12 [1] CRAN (R 4.2.0)
#>  pillar        1.9.0      2023-03-22 [1] CRAN (R 4.2.0)
#>  pkgconfig     2.0.3      2019-09-22 [1] CRAN (R 4.2.0)
#>  purrr       * 1.0.1      2023-01-10 [1] CRAN (R 4.2.0)
#>  R.cache       0.16.0     2022-07-21 [1] CRAN (R 4.2.0)
#>  R.methodsS3   1.8.2      2022-06-13 [1] CRAN (R 4.2.0)
#>  R.oo          1.25.0     2022-06-12 [1] CRAN (R 4.2.0)
#>  R.utils       2.12.2     2022-11-11 [1] CRAN (R 4.2.0)
#>  R6            2.5.1      2021-08-19 [1] CRAN (R 4.2.0)
#>  readr       * 2.1.4      2023-02-10 [1] CRAN (R 4.2.0)
#>  reprex        2.0.2      2022-08-17 [1] CRAN (R 4.2.0)
#>  rlang         1.1.0.9000 2023-03-21 [1] Github (r-lib/rlang@ea2fe5f)
#>  rmarkdown     2.21       2023-03-26 [1] CRAN (R 4.2.2)
#>  rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.0)
#>  scales        1.2.1      2022-08-20 [1] CRAN (R 4.2.0)
#>  sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
#>  stringi       1.7.12     2023-01-11 [1] CRAN (R 4.2.0)
#>  stringr     * 1.5.0      2022-12-02 [1] CRAN (R 4.2.0)
#>  styler        1.9.1      2023-03-04 [1] CRAN (R 4.2.0)
#>  tibble      * 3.2.1      2023-03-20 [1] CRAN (R 4.2.0)
#>  tidyr       * 1.3.0      2023-01-24 [1] CRAN (R 4.2.0)
#>  tidyselect    1.2.0      2022-10-10 [1] CRAN (R 4.2.0)
#>  tidyverse   * 2.0.0      2023-02-22 [1] CRAN (R 4.2.0)
#>  timechange    0.2.0      2023-01-11 [1] CRAN (R 4.2.2)
#>  tzdb          0.3.0      2022-03-28 [1] CRAN (R 4.2.0)
#>  utf8          1.2.3      2023-01-31 [1] CRAN (R 4.2.0)
#>  vctrs         0.6.1      2023-03-22 [1] CRAN (R 4.2.0)
#>  withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
#>  xfun          0.38       2023-03-24 [1] CRAN (R 4.2.0)
#>  yaml          2.3.7      2023-01-23 [1] CRAN (R 4.2.0)
#> 
#>  [1] /Users/jschwartz/Library/R/arm64/4.2/library
#>  [2] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
#> 
#> ──────────────────────────────────────────────────────────────────────────────

eipi10 avatar Apr 01 '23 17:04 eipi10

That probably can't and won't ever work because we can't "see" the expression that built the original object, we only see FUNS

DavisVaughan avatar Apr 01 '23 20:04 DavisVaughan

As a result of my incomplete understanding of how NSE might interact with different ways of passing arguments, I failed to include a separate FUNS object as an example in my initial post.

I just want to point out that in dplyr 1.0.10 you can pass a separate FUNS object into a summary function, without using embrasure, and the summary function works, even when you pass additional columns inside one or more of the functions within FUNS, such as the w argument in weighted.mean. But this approach fails in dplyr 1.1.1. Because I do this often, I ran into this problem almost immediately after I installed 1.1.1. Below are reproducible examples with 1.0.10 and 1.1.1.

You can pass the .fns argument explicitly if you use embrasure, as in my post above, but how can I make the FUNS example below work in 1.1.1 as it does in 1.0.10 (preferably in a way that also works with an explicit .fns argument)?

dplyr 1.0.10: Passing an (unembraced) .fns (directly or as an object) works

library(tidyverse)

fn <- function(data, cols, fns, groups=NULL) {
  data %>% 
    group_by(across({{groups}})) %>% 
    summarise(across(.cols = {{cols}}, .fns = fns))
}

fn(mtcars, 
   cols=c(mpg, hp), 
   fns=c(mean=mean, mean.wt=~weighted.mean(., w=cyl)),
   groups=c(am, vs))
#> `summarise()` has grouped output by 'am'. You can override using the `.groups`
#> argument.
#> # A tibble: 4 × 6
#> # Groups:   am [2]
#>      am    vs mpg_mean mpg_mean.wt hp_mean hp_mean.wt
#>   <dbl> <dbl>    <dbl>       <dbl>   <dbl>      <dbl>
#> 1     0     0     15.0        15.1   194.       194. 
#> 2     0     1     20.7        20.4   102.       105. 
#> 3     1     0     19.8        19.0   181.       198. 
#> 4     1     1     28.4        28.4    80.6       80.6

FUNS = c(mean=mean, mean.wt=~weighted.mean(., w=cyl))

fn(mtcars, 
   cols=c(mpg, hp), 
   fns=FUNS, 
   groups=c(am, vs))
#> `summarise()` has grouped output by 'am'. You can override using the `.groups`
#> argument.
#> # A tibble: 4 × 6
#> # Groups:   am [2]
#>      am    vs mpg_mean mpg_mean.wt hp_mean hp_mean.wt
#>   <dbl> <dbl>    <dbl>       <dbl>   <dbl>      <dbl>
#> 1     0     0     15.0        15.1   194.       194. 
#> 2     0     1     20.7        20.4   102.       105. 
#> 3     1     0     19.8        19.0   181.       198. 
#> 4     1     1     28.4        28.4    80.6       80.6

Created on 2023-04-01 with reprex v2.0.2

Session info
sessioninfo::session_info()
#> ─ Session info ───────────────────────────────────────────────────────────────
#>  setting  value
#>  version  R version 4.2.2 (2022-10-31)
#>  os       macOS Ventura 13.2.1
#>  system   aarch64, darwin20
#>  ui       X11
#>  language (EN)
#>  collate  en_US.UTF-8
#>  ctype    en_US.UTF-8
#>  tz       America/Los_Angeles
#>  date     2023-04-01
#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/ (via rmarkdown)
#> 
#> ─ Packages ───────────────────────────────────────────────────────────────────
#>  package     * version    date (UTC) lib source
#>  cli           3.6.1      2023-03-23 [1] CRAN (R 4.2.0)
#>  colorspace    2.1-0      2023-01-23 [1] CRAN (R 4.2.0)
#>  DBI           1.1.3      2022-06-18 [1] CRAN (R 4.2.0)
#>  digest        0.6.31     2022-12-11 [1] CRAN (R 4.2.0)
#>  dplyr       * 1.0.10     2022-09-01 [1] CRAN (R 4.2.2)
#>  evaluate      0.20       2023-01-17 [1] CRAN (R 4.2.0)
#>  fansi         1.0.4      2023-01-22 [1] CRAN (R 4.2.0)
#>  fastmap       1.1.1      2023-02-24 [1] CRAN (R 4.2.0)
#>  forcats     * 1.0.0      2023-01-29 [1] CRAN (R 4.2.0)
#>  fs            1.6.1      2023-02-06 [1] CRAN (R 4.2.0)
#>  generics      0.1.3      2022-07-05 [1] CRAN (R 4.2.0)
#>  ggplot2     * 3.4.1      2023-02-10 [1] CRAN (R 4.2.0)
#>  glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
#>  gtable        0.3.3      2023-03-21 [1] CRAN (R 4.2.0)
#>  hms           1.1.3      2023-03-21 [1] CRAN (R 4.2.0)
#>  htmltools     0.5.5      2023-03-23 [1] CRAN (R 4.2.0)
#>  knitr         1.42       2023-01-25 [1] CRAN (R 4.2.0)
#>  lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.0)
#>  lubridate   * 1.9.2      2023-02-10 [1] CRAN (R 4.2.0)
#>  magrittr      2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
#>  munsell       0.5.0      2018-06-12 [1] CRAN (R 4.2.0)
#>  pillar        1.9.0      2023-03-22 [1] CRAN (R 4.2.0)
#>  pkgconfig     2.0.3      2019-09-22 [1] CRAN (R 4.2.0)
#>  purrr       * 1.0.1      2023-01-10 [1] CRAN (R 4.2.0)
#>  R.cache       0.16.0     2022-07-21 [1] CRAN (R 4.2.0)
#>  R.methodsS3   1.8.2      2022-06-13 [1] CRAN (R 4.2.0)
#>  R.oo          1.25.0     2022-06-12 [1] CRAN (R 4.2.0)
#>  R.utils       2.12.2     2022-11-11 [1] CRAN (R 4.2.0)
#>  R6            2.5.1      2021-08-19 [1] CRAN (R 4.2.0)
#>  readr       * 2.1.4      2023-02-10 [1] CRAN (R 4.2.0)
#>  reprex        2.0.2      2022-08-17 [1] CRAN (R 4.2.0)
#>  rlang         1.1.0.9000 2023-03-21 [1] Github (r-lib/rlang@ea2fe5f)
#>  rmarkdown     2.21       2023-03-26 [1] CRAN (R 4.2.2)
#>  rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.0)
#>  scales        1.2.1      2022-08-20 [1] CRAN (R 4.2.0)
#>  sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
#>  stringi       1.7.12     2023-01-11 [1] CRAN (R 4.2.0)
#>  stringr     * 1.5.0      2022-12-02 [1] CRAN (R 4.2.0)
#>  styler        1.9.1      2023-03-04 [1] CRAN (R 4.2.0)
#>  tibble      * 3.2.1      2023-03-20 [1] CRAN (R 4.2.0)
#>  tidyr       * 1.3.0      2023-01-24 [1] CRAN (R 4.2.0)
#>  tidyselect    1.2.0      2022-10-10 [1] CRAN (R 4.2.0)
#>  tidyverse   * 2.0.0      2023-02-22 [1] CRAN (R 4.2.0)
#>  timechange    0.2.0      2023-01-11 [1] CRAN (R 4.2.2)
#>  tzdb          0.3.0      2022-03-28 [1] CRAN (R 4.2.0)
#>  utf8          1.2.3      2023-01-31 [1] CRAN (R 4.2.0)
#>  vctrs         0.6.1      2023-03-22 [1] CRAN (R 4.2.0)
#>  withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
#>  xfun          0.38       2023-03-24 [1] CRAN (R 4.2.0)
#>  yaml          2.3.7      2023-01-23 [1] CRAN (R 4.2.0)
#> 
#>  [1] /Users/jschwartz/Library/R/arm64/4.2/library
#>  [2] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
#> 
#> ──────────────────────────────────────────────────────────────────────────────

dplyr 1.1.1: Passing an (unembraced) .fns (directly or as an object) fails

library(tidyverse)

fn <- function(data, cols, fns, groups=NULL) {
  data %>% 
    group_by(across({{groups}})) %>% 
    summarise(across(.cols = {{cols}}, .fns = fns))
}

fn(mtcars, 
   cols=c(mpg, hp), 
   fns=c(mean=mean, mean.wt=~weighted.mean(., w=cyl)),
   groups=c(am, vs))
#> Error in `summarise()`:
#> ℹ In argument: `across(.cols = c(mpg, hp), .fns = fns)`.
#> ℹ In group 1: `am = 0`, `vs = 0`.
#> Caused by error in `across()`:
#> ! Can't compute column `mpg_mean.wt`.
#> Caused by error in `weighted.mean.default()`:
#> ! object 'cyl' not found
#> Backtrace:
#>      ▆
#>   1. ├─global fn(...)
#>   2. │ └─data %>% group_by(across({{ groups }})) %>% ...
#>   3. ├─dplyr::summarise(...)
#>   4. ├─dplyr:::summarise.grouped_df(...)
#>   5. │ └─dplyr:::summarise_cols(.data, dplyr_quosures(...), by, "summarise")
#>   6. │   ├─base::withCallingHandlers(...)
#>   7. │   └─dplyr:::map(quosures, summarise_eval_one, mask = mask)
#>   8. │     └─base::lapply(.x, .f, ...)
#>   9. │       └─dplyr (local) FUN(X[[i]], ...)
#>  10. │         ├─base::withCallingHandlers(...)
#>  11. │         └─mask$eval_all_summarise(quo)
#>  12. │           └─dplyr (local) eval()
#>  13. ├─global `<rlng_lm_>`(mpg)
#>  14. │ ├─stats::weighted.mean(., w = cyl)
#>  15. │ └─stats:::weighted.mean.default(., w = cyl)
#>  16. └─base::.handleSimpleError(...)
#>  17.   └─dplyr (local) h(simpleError(msg, call))
#>  18.     └─rlang::abort(msg, call = call("across"), parent = cnd)

FUNS = c(mean=mean, mean.wt=~weighted.mean(., w=cyl))

fn(mtcars, 
   cols=c(mpg, hp), 
   fns=FUNS, 
   groups=c(am, vs))
#> Error in `summarise()`:
#> ℹ In argument: `across(.cols = c(mpg, hp), .fns = fns)`.
#> ℹ In group 1: `am = 0`, `vs = 0`.
#> Caused by error in `across()`:
#> ! Can't compute column `mpg_mean.wt`.
#> Caused by error in `weighted.mean.default()`:
#> ! object 'cyl' not found
#> Backtrace:
#>      ▆
#>   1. ├─global fn(mtcars, cols = c(mpg, hp), fns = FUNS, groups = c(am, vs))
#>   2. │ └─data %>% group_by(across({{ groups }})) %>% ...
#>   3. ├─dplyr::summarise(...)
#>   4. ├─dplyr:::summarise.grouped_df(...)
#>   5. │ └─dplyr:::summarise_cols(.data, dplyr_quosures(...), by, "summarise")
#>   6. │   ├─base::withCallingHandlers(...)
#>   7. │   └─dplyr:::map(quosures, summarise_eval_one, mask = mask)
#>   8. │     └─base::lapply(.x, .f, ...)
#>   9. │       └─dplyr (local) FUN(X[[i]], ...)
#>  10. │         ├─base::withCallingHandlers(...)
#>  11. │         └─mask$eval_all_summarise(quo)
#>  12. │           └─dplyr (local) eval()
#>  13. ├─global `<rlng_lm_>`(mpg)
#>  14. │ ├─stats::weighted.mean(., w = cyl)
#>  15. │ └─stats:::weighted.mean.default(., w = cyl)
#>  16. └─base::.handleSimpleError(...)
#>  17.   └─dplyr (local) h(simpleError(msg, call))
#>  18.     └─rlang::abort(msg, call = call("across"), parent = cnd)

Created on 2023-04-01 with reprex v2.0.2

Session info
sessioninfo::session_info()
#> ─ Session info ───────────────────────────────────────────────────────────────
#>  setting  value
#>  version  R version 4.2.2 (2022-10-31)
#>  os       macOS Ventura 13.2.1
#>  system   aarch64, darwin20
#>  ui       X11
#>  language (EN)
#>  collate  en_US.UTF-8
#>  ctype    en_US.UTF-8
#>  tz       America/Los_Angeles
#>  date     2023-04-01
#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/ (via rmarkdown)
#> 
#> ─ Packages ───────────────────────────────────────────────────────────────────
#>  package     * version    date (UTC) lib source
#>  cli           3.6.1      2023-03-23 [1] CRAN (R 4.2.0)
#>  colorspace    2.1-0      2023-01-23 [1] CRAN (R 4.2.0)
#>  digest        0.6.31     2022-12-11 [1] CRAN (R 4.2.0)
#>  dplyr       * 1.1.1      2023-03-22 [1] CRAN (R 4.2.2)
#>  evaluate      0.20       2023-01-17 [1] CRAN (R 4.2.0)
#>  fansi         1.0.4      2023-01-22 [1] CRAN (R 4.2.0)
#>  fastmap       1.1.1      2023-02-24 [1] CRAN (R 4.2.0)
#>  forcats     * 1.0.0      2023-01-29 [1] CRAN (R 4.2.0)
#>  fs            1.6.1      2023-02-06 [1] CRAN (R 4.2.0)
#>  generics      0.1.3      2022-07-05 [1] CRAN (R 4.2.0)
#>  ggplot2     * 3.4.1      2023-02-10 [1] CRAN (R 4.2.0)
#>  glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
#>  gtable        0.3.3      2023-03-21 [1] CRAN (R 4.2.0)
#>  hms           1.1.3      2023-03-21 [1] CRAN (R 4.2.0)
#>  htmltools     0.5.5      2023-03-23 [1] CRAN (R 4.2.0)
#>  knitr         1.42       2023-01-25 [1] CRAN (R 4.2.0)
#>  lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.0)
#>  lubridate   * 1.9.2      2023-02-10 [1] CRAN (R 4.2.0)
#>  magrittr      2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
#>  munsell       0.5.0      2018-06-12 [1] CRAN (R 4.2.0)
#>  pillar        1.9.0      2023-03-22 [1] CRAN (R 4.2.0)
#>  pkgconfig     2.0.3      2019-09-22 [1] CRAN (R 4.2.0)
#>  purrr       * 1.0.1      2023-01-10 [1] CRAN (R 4.2.0)
#>  R.cache       0.16.0     2022-07-21 [1] CRAN (R 4.2.0)
#>  R.methodsS3   1.8.2      2022-06-13 [1] CRAN (R 4.2.0)
#>  R.oo          1.25.0     2022-06-12 [1] CRAN (R 4.2.0)
#>  R.utils       2.12.2     2022-11-11 [1] CRAN (R 4.2.0)
#>  R6            2.5.1      2021-08-19 [1] CRAN (R 4.2.0)
#>  readr       * 2.1.4      2023-02-10 [1] CRAN (R 4.2.0)
#>  reprex        2.0.2      2022-08-17 [1] CRAN (R 4.2.0)
#>  rlang         1.1.0.9000 2023-03-21 [1] Github (r-lib/rlang@ea2fe5f)
#>  rmarkdown     2.21       2023-03-26 [1] CRAN (R 4.2.2)
#>  rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.0)
#>  scales        1.2.1      2022-08-20 [1] CRAN (R 4.2.0)
#>  sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
#>  stringi       1.7.12     2023-01-11 [1] CRAN (R 4.2.0)
#>  stringr     * 1.5.0      2022-12-02 [1] CRAN (R 4.2.0)
#>  styler        1.9.1      2023-03-04 [1] CRAN (R 4.2.0)
#>  tibble      * 3.2.1      2023-03-20 [1] CRAN (R 4.2.0)
#>  tidyr       * 1.3.0      2023-01-24 [1] CRAN (R 4.2.0)
#>  tidyselect    1.2.0      2022-10-10 [1] CRAN (R 4.2.0)
#>  tidyverse   * 2.0.0      2023-02-22 [1] CRAN (R 4.2.0)
#>  timechange    0.2.0      2023-01-11 [1] CRAN (R 4.2.2)
#>  tzdb          0.3.0      2022-03-28 [1] CRAN (R 4.2.0)
#>  utf8          1.2.3      2023-01-31 [1] CRAN (R 4.2.0)
#>  vctrs         0.6.1      2023-03-22 [1] CRAN (R 4.2.0)
#>  withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
#>  xfun          0.38       2023-03-24 [1] CRAN (R 4.2.0)
#>  yaml          2.3.7      2023-01-23 [1] CRAN (R 4.2.0)
#> 
#>  [1] /Users/jschwartz/Library/R/arm64/4.2/library
#>  [2] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
#> 
#> ──────────────────────────────────────────────────────────────────────────────

eipi10 avatar Apr 02 '23 02:04 eipi10

I believe that the new behavior was introduced in https://github.com/tidyverse/dplyr/pull/6550 IMO, the new behavior is better, it removes ambiguities. For example, in the above example,

fn <- function(data, cols, fns, groups=NULL) {
  data %>% 
    group_by(across({{groups}})) %>% 
    summarise(across(.cols = {{cols}}, .fns = fns))
}

fn(mtcars, 
   cols=c(mpg, hp), 
   fns=c(mean=mean, mean.wt=~weighted.mean(., w=cyl)),
   groups=c(am, vs))

It is unclear if cyl should be from the data frame or a global variable.

randy3k avatar Apr 06 '23 08:04 randy3k

I would expect cyl to be picked up from the data frame in that example, consistently with dplyr semantics. This can be achieved by interpolating .fns with .fns = {{ .fns }}. We'll test and document this as an official pattern.

lionel- avatar Apr 06 '23 09:04 lionel-

@lionel- the code below fails in dplyr 1.1.1 even though it uses embracing operator, so I think I'm not understanding your previous comment. Is there a different pattern I should be using to pass FUNS into a summarizing function in a way that will work with weighted.mean (or other functions that similarly require ancillary columns to be passed into the .fns argument)?

library(tidyverse)

fn <- function(data, cols, fns, groups=NULL) {
  data %>% 
    group_by(across({{groups}})) %>% 
    summarise(across(.cols = {{cols}}, .fns = {{fns}}))
}

FUNS = c(mean=mean, mean.wt=~weighted.mean(., w=cyl))

fn(mtcars, 
   cols=c(mpg, hp), 
   fns=FUNS, 
   groups=c(am, vs))
#> Error in `summarise()`:
#> ℹ In argument: `across(.cols = c(mpg, hp), .fns = FUNS)`.
#> ℹ In group 1: `am = 0`, `vs = 0`.
#> Caused by error in `across()`:
#> ! Can't compute column `mpg_mean.wt`.
#> Caused by error in `weighted.mean.default()`:
#> ! object 'cyl' not found
#> Backtrace:
#>      ▆
#>   1. ├─global fn(mtcars, cols = c(mpg, hp), fns = FUNS, groups = c(am, vs))
#>   2. │ └─data %>% group_by(across({{ groups }})) %>% ...
#>   3. ├─dplyr::summarise(...)
#>   4. ├─dplyr:::summarise.grouped_df(...)
#>   5. │ └─dplyr:::summarise_cols(.data, dplyr_quosures(...), by, "summarise")
#>   6. │   ├─base::withCallingHandlers(...)
#>   7. │   └─dplyr:::map(quosures, summarise_eval_one, mask = mask)
#>   8. │     └─base::lapply(.x, .f, ...)
#>   9. │       └─dplyr (local) FUN(X[[i]], ...)
#>  10. │         ├─base::withCallingHandlers(...)
#>  11. │         └─mask$eval_all_summarise(quo)
#>  12. │           └─dplyr (local) eval()
#>  13. ├─global `<rlng_lm_>`(mpg)
#>  14. │ ├─stats::weighted.mean(., w = cyl)
#>  15. │ └─stats:::weighted.mean.default(., w = cyl)
#>  16. └─base::.handleSimpleError(...)
#>  17.   └─dplyr (local) h(simpleError(msg, call))
#>  18.     └─rlang::abort(msg, call = call("across"), parent = cnd)

Created on 2023-04-06 with reprex v2.0.2

Session info
sessioninfo::session_info()
#> ─ Session info ───────────────────────────────────────────────────────────────
#>  setting  value
#>  version  R version 4.2.3 (2023-03-15)
#>  os       macOS Ventura 13.2.1
#>  system   aarch64, darwin20
#>  ui       X11
#>  language (EN)
#>  collate  en_US.UTF-8
#>  ctype    en_US.UTF-8
#>  tz       America/Los_Angeles
#>  date     2023-04-06
#>  pandoc   2.19.2 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/ (via rmarkdown)
#> 
#> ─ Packages ───────────────────────────────────────────────────────────────────
#>  package     * version    date (UTC) lib source
#>  cli           3.6.1      2023-03-23 [1] CRAN (R 4.2.0)
#>  colorspace    2.1-0      2023-01-23 [1] CRAN (R 4.2.0)
#>  digest        0.6.31     2022-12-11 [1] CRAN (R 4.2.0)
#>  dplyr       * 1.1.1      2023-03-22 [1] CRAN (R 4.2.2)
#>  evaluate      0.20       2023-01-17 [1] CRAN (R 4.2.0)
#>  fansi         1.0.4      2023-01-22 [1] CRAN (R 4.2.0)
#>  fastmap       1.1.1      2023-02-24 [1] CRAN (R 4.2.0)
#>  forcats     * 1.0.0      2023-01-29 [1] CRAN (R 4.2.0)
#>  fs            1.6.1      2023-02-06 [1] CRAN (R 4.2.0)
#>  generics      0.1.3      2022-07-05 [1] CRAN (R 4.2.0)
#>  ggplot2     * 3.4.2      2023-04-03 [1] CRAN (R 4.2.0)
#>  glue          1.6.2      2022-02-24 [1] CRAN (R 4.2.0)
#>  gtable        0.3.3      2023-03-21 [1] CRAN (R 4.2.0)
#>  hms           1.1.3      2023-03-21 [1] CRAN (R 4.2.0)
#>  htmltools     0.5.5      2023-03-23 [1] CRAN (R 4.2.0)
#>  knitr         1.42       2023-01-25 [1] CRAN (R 4.2.0)
#>  lifecycle     1.0.3      2022-10-07 [1] CRAN (R 4.2.0)
#>  lubridate   * 1.9.2      2023-02-10 [1] CRAN (R 4.2.0)
#>  magrittr      2.0.3      2022-03-30 [1] CRAN (R 4.2.0)
#>  munsell       0.5.0      2018-06-12 [1] CRAN (R 4.2.0)
#>  pillar        1.9.0      2023-03-22 [1] CRAN (R 4.2.0)
#>  pkgconfig     2.0.3      2019-09-22 [1] CRAN (R 4.2.0)
#>  purrr       * 1.0.1      2023-01-10 [1] CRAN (R 4.2.0)
#>  R.cache       0.16.0     2022-07-21 [1] CRAN (R 4.2.0)
#>  R.methodsS3   1.8.2      2022-06-13 [1] CRAN (R 4.2.0)
#>  R.oo          1.25.0     2022-06-12 [1] CRAN (R 4.2.0)
#>  R.utils       2.12.2     2022-11-11 [1] CRAN (R 4.2.0)
#>  R6            2.5.1      2021-08-19 [1] CRAN (R 4.2.0)
#>  readr       * 2.1.4      2023-02-10 [1] CRAN (R 4.2.0)
#>  reprex        2.0.2      2022-08-17 [1] CRAN (R 4.2.0)
#>  rlang         1.1.0.9000 2023-03-21 [1] Github (r-lib/rlang@ea2fe5f)
#>  rmarkdown     2.21       2023-03-26 [1] CRAN (R 4.2.2)
#>  rstudioapi    0.14       2022-08-22 [1] CRAN (R 4.2.0)
#>  scales        1.2.1      2022-08-20 [1] CRAN (R 4.2.0)
#>  sessioninfo   1.2.2      2021-12-06 [1] CRAN (R 4.2.0)
#>  stringi       1.7.12     2023-01-11 [1] CRAN (R 4.2.0)
#>  stringr     * 1.5.0      2022-12-02 [1] CRAN (R 4.2.0)
#>  styler        1.9.1      2023-03-04 [1] CRAN (R 4.2.0)
#>  tibble      * 3.2.1      2023-03-20 [1] CRAN (R 4.2.0)
#>  tidyr       * 1.3.0      2023-01-24 [1] CRAN (R 4.2.0)
#>  tidyselect    1.2.0      2022-10-10 [1] CRAN (R 4.2.0)
#>  tidyverse   * 2.0.0      2023-02-22 [1] CRAN (R 4.2.0)
#>  timechange    0.2.0      2023-01-11 [1] CRAN (R 4.2.2)
#>  tzdb          0.3.0      2022-03-28 [1] CRAN (R 4.2.0)
#>  utf8          1.2.3      2023-01-31 [1] CRAN (R 4.2.0)
#>  vctrs         0.6.1      2023-03-22 [1] CRAN (R 4.2.0)
#>  withr         2.5.0      2022-03-03 [1] CRAN (R 4.2.0)
#>  xfun          0.38       2023-03-24 [1] CRAN (R 4.2.0)
#>  yaml          2.3.7      2023-01-23 [1] CRAN (R 4.2.0)
#> 
#>  [1] /Users/jschwartz/Library/R/arm64/4.2/library
#>  [2] /Library/Frameworks/R.framework/Versions/4.2-arm64/Resources/library
#> 
#> ──────────────────────────────────────────────────────────────────────────────

eipi10 avatar Apr 06 '23 23:04 eipi10

@eipi10 It is still possible, but you need to defuse and inject the expression.

FUNS = quo(c(mean=mean, mean.wt=~weighted.mean(., w=cyl)))

fn(mtcars, 
   cols=c(mpg, hp), 
   fns=!!FUNS, 
   groups=c(am, vs))

randy3k avatar Apr 07 '23 02:04 randy3k

Thanks @randy3k!

eipi10 avatar Apr 07 '23 22:04 eipi10

I found this after experiencing the identical issue- needing to use weighted.mean with a data-variable for the weights in an across, with weighted.mean being one of many possible user-supplied functions. These functions are typically defined in a list by a user (or programatically), which is then passed to a function essentially the same as fn above, essentially identically to @randy3k 's comment above.

While the solution works, it is causing headaches for users, who have to remember to wrap their list of functions in rlang::quo sometimes and use !! in the call. In addition, if a list of functions is generated programatically, getting that quo wrapper is not straightforward.

I see @randy3k 's point about ambiguities, but I wonder if there's a way to explicitly remove them while avoiding the need to wrap the whole set of functions in quo. A solution that allowed an explict data reference would remove the ambiguity, e.g. FUNS <- list(mean = mean, mean.wt = ~weighted.mean(., w = .data$cyl). Is that possible?

I've tried to get that to work in a few different ways by using eval_tidy to provide the .data pronoun inside the summary(across)) with no success and maybe it just doesn't work- I get confused quickly trying to understand what is actually happening with the stack and what can be referenced by the time we're inside the summarize(across()).

galenholt avatar Aug 22 '23 23:08 galenholt

I used rlang::as_function(). It seemed to work. But I am not too sure of the implications.

olivroy avatar Oct 04 '23 14:10 olivroy

Just checking back here to see if there is now (or will eventually be) a better way to pass function arguments within across. By "better," I mean better than having to remember to defuse the function(s) by wrapping in quo and then later inject with !!.

eipi10 avatar May 31 '24 15:05 eipi10