R dplyr summarise across remaining columns

Viewed 46

Using R's dplyr is there a clean way to use the across function to select variables not already captured in other across statements?

For example, I could have a data set that I want to summarise on a 1-to-1 basis (i.e. the output columns have the same structure as the input data set), applying the first function to the character fields, the mean function to a select number of numeric fields and then sum to all remaining fields.

Typically I have a large number of columns with various selection methods, so explicitly working out the remaining columns can be overall onerous. To date, I have found workarounds given the particular data set I am working with but having a generic solution would be very useful.

The code below shows what I would like to run and the resulting output. remaining_columns() is made up.

library(dplyr)

first.if.unique = function(x) if(length(unique(x))==1) x[1] else NA

x = starwars %>% select(species,sex,mass,height) %>% head(10)

cols_to_average = c("mass")

x %>%
  group_by(sex) %>%
  summarise(
    across(where(is.character),first.if.unique),
    across(any_of(cols_to_average),mean),
    across(remaining_columns(),sum)
  )
  

Input / Output data sets:

> print(x)
    # A tibble: 10 x 4
       species sex     mass height
       <chr>   <chr>  <dbl>  <int>
     1 Human   male      77    172
     2 Droid   none      75    167
     3 Droid   none      32     96
     4 Human   male     136    202
     5 Human   female    49    150
     6 Human   male     120    178
     7 Human   female    75    165
     8 Droid   none      32     97
     9 Human   male      84    183
    10 Human   male      77    182

> print(y)
    # A tibble: 3 x 4
      sex    species  mass height
      <chr>  <chr>   <dbl>  <int>
    1 female Human    62      315
    2 male   Human    98.8    917
    3 none   Droid    46.3    360
1 Answers

I personally don't see a way to achieve this apart from specifying the negative previous selections.

x %>%
  group_by(sex) %>%
  summarise(
    across(where(is.character),first.if.unique),
    across(any_of(cols_to_average),mean),
    across(!where(is.character)&!any_of(cols_to_average),sum)
  )

# A tibble: 3 x 4
  sex    species  mass height
  <chr>  <chr>   <dbl>  <int>
1 female Human    62      315
2 male   Human    98.8    917
3 none   Droid    46.3    360
Related