Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add to curly-curly argument

Tags:

r

rlang

tidyverse

I am trying to take this canonical example for how the curly-curly operator works

mean_by <- function(data, by, var) {
  data %>%
    dplyr::group_by({{ by }}) %>%
    dplyr::summarise(avg = mean({{ var }}, na.rm = TRUE))
}

mtcars %>% mean_by(by = cyl, var = disp)

and modify it by adding an additional variable to the grouping. Alas, I may never understand what that operator actually does; my attempts to do things like

mean_by <- function(data, by, var) {
  data %>%
    dplyr::group_by({{ c(by, hp) }}) %>%
    dplyr::summarise(avg = mean({{ var }}, na.rm = TRUE))
}

mtcars %>% mean_by(by = cyl, var = disp)

clearly aren't working

like image 768
RoyalTS Avatar asked Oct 15 '25 15:10

RoyalTS


2 Answers

A few years ago dplyr added pick to let people pass off {{}} to data masking functions. So you can do something like this.

library(dplyr)

group_mean = \(data, by, var, ...) {
  data |> 
   group_by(pick({{by}}), ...) |> 
   summarise(mean = mean({{var}}))
}

group_mean(starwars, by = c(homeworld, species), sex, var = mass)
#> `summarise()` has grouped output by 'homeworld', 'species'. You can override
#> using the `.groups` argument.
#> # A tibble: 64 × 4
#> # Groups:   homeworld, species [57]
#>    homeworld      species   sex     mean
#>    <chr>          <chr>     <chr>  <dbl>
#>  1 Alderaan       Human     female    49
#>  2 Alderaan       Human     male      NA
#>  3 Aleen Minor    Aleena    male      15
#>  4 Bespin         Human     male      79
#>  5 Bestine IV     <NA>      <NA>     110
#>  6 Cato Neimoidia Neimodian male      90
#>  7 Cerea          Cerean    male      82
#>  8 Champala       Chagrian  male      NA
#>  9 Chandrila      Human     female    NA
#> 10 Concord Dawn   Human     male      79
#> # ℹ 54 more rows

Created on 2024-09-18 with reprex v2.1.0

like image 146
Josh Allen Avatar answered Oct 17 '25 05:10

Josh Allen


It would be possible to do this using rlang::enquos() to defuse multiple expressions:

dplyr::group_by(hp, !!!rlang::enquos(by))

However, as this Tidyverse blog post states:

If you would like to pass multiple arguments to a data-masking verb, pass ... directly

So in your case, to group by hp and whatever else the caller passes:

mean_by_hp <- function(data, ..., var) {
    data |>
        dplyr::group_by(hp, ...) |>
        dplyr::summarise(avg = mean({{ var }}, na.rm = TRUE))
}


mean_by_hp(mtcars, cyl, var = disp)
#       hp   cyl   avg
#    <dbl> <dbl> <dbl>
#  1    52     4  75.7
#  2    62     4 147.
#  3    65     4  71.1
#  4    66     4  78.8
#  5    91     4 120.
#  6    93     4 108
#  7    95     4 141.
#  8    97     4 120.
#  9   105     6 225
# 10   109     4 121
like image 25
SamR Avatar answered Oct 17 '25 07:10

SamR



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!