Applying a conditional across multiple columns using case_when in dplyr 1.0.0

Viewed 521

I can't figure this one out. Say I want to create a conditional variable that returns a hit every time any row in a data frame has a column that contains the target.

Always easier to illustrate with examples. Here's the toy data

set.seed(10)
d <- data.frame(a = sample(x = letters[1:2], 
                           size = 10,
                           replace = T),
                b = sample(x = letters[1:2], 
                           size = 10,
                           replace = T),
                c = sample(x = letters[1:2], 
                           size = 10,
                           replace = T))

d

# output
#    a b c
# 1  a a a
# 2  a b b
# 3  b a b
# 4  b a a
# 5  b b b
# 6  a a a
# 7  b b a
# 8  b b b
# 9  a b b
# 10 a a a

Now I want to create a column that returns anyA when there is an a in any of the rows, and returns noA when there is no a present. The output would look like this

#    a b c anyA
# 1  a a a anyA
# 2  a b b anyA
# 3  b a b anyA
# 4  b a a anyA
# 5  b b b  noA
# 6  a a a anyA
# 7  b b a anyA
# 8  b b b  noA
# 9  a b b anyA
# 10 a a a anyA

I tried the following code, using, mutate, rowwise, c_across and case_when

d %>%
  rowwise() %>%
    mutate(anyA = case_when(c_across(cols = everything(),
                            any(.x == "a") ~ "anyA",
                            TRUE ~ "noA")))

But it didn't work. I don't really know what I'm doing. Any ideas?

p.s. I specifically don't want to have to name all the columns in case_when. In my real-life example there are a lot of columns to apply the conditional to.

2 Answers

You can use rowwise and c_across as -

library(dplyr)

d %>%
  rowwise() %>%
  mutate(anyA = case_when(any(c_across() == "a") ~ "anyA",
                              TRUE ~ "noA")) %>%
  ungroup

#   a     b     c     anyA 
#   <chr> <chr> <chr> <chr>
# 1 a     a     a     anyA 
# 2 a     b     b     anyA 
# 3 b     a     b     anyA 
# 4 b     a     a     anyA 
# 5 b     b     b     noA  
# 6 a     a     a     anyA 
# 7 b     b     a     anyA 
# 8 b     b     b     noA  
# 9 a     b     b     anyA 
#10 a     a     a     anyA 

c_across() by default has cols = everything().


Or without rowwise, using if_any which should be faster.

d %>% mutate(anyA = if_else(if_any(.fns = ~. == 'a'), "anyA", "noA"))

We can use vectorized if_any

library(dplyr)
d %>%
    mutate(anyA = c("noA", "anyA")[1 + (if_any(everything(), `==`, "a"))])
   a b c anyA
1  a a a anyA
2  a b b anyA
3  b a b anyA
4  b a a anyA
5  b b b  noA
6  a a a anyA
7  b b a anyA
8  b b b  noA
9  a b b anyA
10 a a a anyA
Related