tidyverse / dplyr

dplyr: A grammar of data manipulation
https://dplyr.tidyverse.org/
Other
4.77k stars 2.12k forks source link

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

Open eipi10 opened 1 year ago

eipi10 commented 1 year ago

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
DavisVaughan commented 1 year ago

Minimal reprex

1.0.10:

# pak::pak("tidyverse/dplyr@v1.0.10")
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 commented 1 year ago

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 {{

eipi10 commented 1 year ago

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 ``` r 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 commented 1 year ago

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 ``` r 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 #> #> ────────────────────────────────────────────────────────────────────────────── ```
DavisVaughan commented 1 year ago

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

eipi10 commented 1 year ago

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 ``` r 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 ``` r 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 #> #> ────────────────────────────────────────────────────────────────────────────── ```
randy3k commented 1 year ago

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.

lionel- commented 1 year ago

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.

eipi10 commented 1 year ago

@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 ``` r 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 #> #> ────────────────────────────────────────────────────────────────────────────── ```
randy3k commented 1 year ago

@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))
eipi10 commented 1 year ago

Thanks @randy3k!

galenholt commented 1 year ago

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()).

olivroy commented 1 year ago

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

eipi10 commented 5 months ago

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 !!.