rdplyrconditional-statementstidyverse

Is there a efficient way to mutate only on rows that meet a condition? Think mutate(when(condition))


I am looking to apply a mutate only when certain conditions are met.

I know I can do this...

data2 <- data1 %>%
   group_by(a, b) %>%
   mutate(
      var1 = case_when(
         condition ~ TRUE,
         TRUE ~ FALSE,
         NA
         ),
      var2 = case_when(
         condition ~ TRUE,
         max(var28),
         var2
         ),
      var3 = case_when(
         condition ~ TRUE,
         "happy",
         var3
         ),
...more vars here....
)

What I would like is something that looks like this...

data2 <- data1 %>%
   group_by(a, b) %>%
   mutate(
      when(condition),
      var1 = FALSE,
      var2 = max(var28),
      var3 = "happy",
...more vars here....
)

Unfortunately mutate(across(when(condition))) did not work.

Any suggesions?


Solution

  • There is no functionality to do this in mutate, but Romain Francois has shared a function you could define yourself which does this:

    library(dplyr, warn.conflicts = F)
    
    mutate_when <- function(.data, when, ...) {
      dots <- enquos(...)
      names <- names(dots)
      
      mutate(.data, {
        test <- {{ when }}
        
        changed <- data.frame(!!!dots, stringsAsFactors = FALSE)
        out <- across(all_of(names))
        # assuming `changed` and `out` have the same data frame type
    
        out[test, ] <- changed[test, ]
        out
      })
      
    }
    
    tibble(x = 1:4, y = 1:4) %>% 
      mutate_when(x < 4, x = -x, y = -y)
    #> # A tibble: 4 × 2
    #>       x     y
    #>   <int> <int>
    #> 1    -1    -1
    #> 2    -2    -2
    #> 3    -3    -3
    #> 4     4     4
    

    Created on 2021-11-23 by the reprex package (v2.0.1)

    Now that case_when() accepts data frames on the right hand side of ~, you can use case_when() to make a mutate_when().

    mutate_when <- function(.data, .when, ...) {
      dots <- enquos(...)
      names <- names(dots)
      new_cols <- transmute(.data, ...)
      mutate(.data, case_when(
        !!enexpr(.when) ~ new_cols,
        .default = pick(names)
      ))
    }