2

Conditional mutating in dplyr is "easy" (kind of) using if_else():

can dplyr package be used for conditional mutating?

However, suppose I want to mutate columns x,y,z at the same time, based on a logical condition C which depends on column group: also, the mutation happens only if C is TRUE, otherwise x,y,z stay the same. How can I do that? In base R, it's straightforward:

n <- 5 
k <- 10
my_labels <- LETTERS[1:5]

foobar <- data.frame(group = gl(n, k, labels = my_labels), x = runif(n*k), y=rnorm(n*k), z = rpois(n*k, 1), month_name = (rep(month.name[1:k], n)))

# mutate x, y, z, only if the condition on group is TRUE
foobar[foobar$group %in% c("B","E"), c("x", "y", "z")] <- NA

How do I achieve the same result with dplyr (if possible)?

DeltaIV
  • 4,773
  • 12
  • 39
  • 86

2 Answers2

4

We can use replace within mutate_at

library(dplyr)
foobar %>% 
   mutate_at(vars(x, y, z),
       funs(replace(., group %in% c("B", "E"), NA)))

If we want to use if_else

foobar %>% 
  mutate_at(vars(x, y, z),
      funs(if_else(group %in% c("B", "E"), NA_real_, as.numeric(.))))
akrun
  • 874,273
  • 37
  • 540
  • 662
  • interesting: is `replace` a base R function'? I will have to read its help. – DeltaIV Jun 17 '18 at 13:37
  • 1
    @DeltaIV Yes, it is a base R function. It is more convenient than `ifelse` or the dplyr::if_else (which does additional type check) as we need to only replace some values with a single value – akrun Jun 17 '18 at 13:38
  • cool! I suppose I can also wrap it in a function outside `funs`, right? I mean, the following should work: `replacer <- function(x) replace(x, group %in% c("B", "E"), NA)` and then `...mutate_at(vars(x, y, z), replacer) `. Correct? – DeltaIV Jun 17 '18 at 13:40
  • @DeltaIV Instead of having subset of functions, I would create a single function and do this i.e. `f1 <- function(dat, varS, grp) {dat %>% mutate_at(vars(varS), funs(replace(., !! rlang::sym(grp %in% c("B", "E"), NA)))}` – akrun Jun 17 '18 at 20:46
3

Using dplyr::across available from dplyr 1.0.0 onward, you could do:

foobar %>% 
  mutate(across(c(x, y, z), 
         ~ if_else(group %in% c("B", "E"), NA_real_, as.numeric(.x))))
climatestudent
  • 459
  • 4
  • 13