Mutate, across, and case_when
Asked Answered
L

3

8

I am having some trouble getting mutate, across, and case_when to function properly, I've recreated a simple version of my problem here:

a <- c(1:10)
b <- c(2:11)
c <- c(3:12)

test <- tibble(a, b, c)

# A tibble: 10 x 3
       a     b     c
   <int> <int> <int>
 1     1     2     3
 2     2     3     4
 3     3     4     5
 4     4     5     6
 5     5     6     7
 6     6     7     8
 7     7     8     9
 8     8     9    10
 9     9    10    11
10    10    11    12

My goal is to replace all of the 3's with 4's, and keep everything else the same. I have the following code:

test_1 <- 
  test %>% 
  mutate(across(a:c, ~ case_when(. == 3 ~ 4)))

# A tibble: 10 x 3
       a     b     c
   <dbl> <dbl> <dbl>
 1    NA    NA     4
 2    NA     4    NA
 3     4    NA    NA
 4    NA    NA    NA
 5    NA    NA    NA
 6    NA    NA    NA
 7    NA    NA    NA
 8    NA    NA    NA
 9    NA    NA    NA
10    NA    NA    NA

It's close but I get NA values where I want to maintain the value in the original tibble. How do I maintain the original values using the mutate across structure?

Thank you in advance!

Lerner answered 17/1, 2021 at 0:32 Comment(2)
If a condition is not satisfied in a case_when statement it returns NA.So if you don't want NA when . != 3 you need to specify it explicitly by TRUE ~ ..Outspread
Thanks @RonakShah for the explanation - personally, I find this syntax really unintuitive because you would expect FALSE ~ . to specify when a condition is NOT met. You can only understand it if you interpret all cases before as if-else statements, so the last statement catches all conditions which were NOT met (and therefore TRUE for NOT being met).Erratum
F
1

This also works:

a <- c(1:10)
b <- c(2:11)
c <- c(3:12)

tibble(a, b, c) %>% 
  modify(~ ifelse(. == 3, 4, .))

# A tibble: 10 x 3
       a     b     c
   <dbl> <dbl> <dbl>
 1     1     2     4
 2     2     4     4
 3     4     4     5
 4     4     5     6
 5     5     6     7
 6     6     7     8
 7     7     8     9
 8     8     9    10
 9     9    10    11
10    10    11    12
Fated answered 7/2, 2021 at 22:39 Comment(0)
M
11

What about this?

> test %>%
+   mutate(across(a:c, ~ case_when(. == 3 ~ 4, TRUE ~ 1 * (.))))
# A tibble: 10 x 3
       a     b     c
   <dbl> <dbl> <dbl>
 1     1     2     4
 2     2     4     4
 3     4     4     5
 4     4     5     6
 5     5     6     7
 6     6     7     8
 7     7     8     9
 8     8     9    10
 9     9    10    11
10    10    11    12

or

> test %>%
+   replace(. == 3, 4)
# A tibble: 10 x 3
       a     b     c
   <int> <int> <int>
 1     1     2     4
 2     2     4     4
 3     4     4     5
 4     4     5     6
 5     5     6     7
 6     6     7     8
 7     7     8     9
 8     8     9    10
 9     9    10    11
10    10    11    12
Motorbus answered 17/1, 2021 at 0:39 Comment(1)
Your first response is spot on! Thank you. Replace is another great idea however it is a bit tricky if you need to replace multiple values across columns a:b, say, and not c. (At least I couldn't seem to figure it out...)Lerner
P
2

In base R, we can do

test[test ==3] <- 4
Palmira answered 17/1, 2021 at 18:10 Comment(0)
F
1

This also works:

a <- c(1:10)
b <- c(2:11)
c <- c(3:12)

tibble(a, b, c) %>% 
  modify(~ ifelse(. == 3, 4, .))

# A tibble: 10 x 3
       a     b     c
   <dbl> <dbl> <dbl>
 1     1     2     4
 2     2     4     4
 3     4     4     5
 4     4     5     6
 5     5     6     7
 6     6     7     8
 7     7     8     9
 8     8     9    10
 9     9    10    11
10    10    11    12
Fated answered 7/2, 2021 at 22:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.