'tidyverse and dplyr: Conditional replacement of values in a column based on other column [duplicate]

I want to mutate a column A4 by A3 but reducing value of A3 by 1 if Total == 63. What am I doing wrong here?

tb1 %>% 
  mutate(A4 = replace(A3, Total == 63, A3-1))

The complete code with data is here

library(tidyverse)

tb1 <-
structure(
  list(
    A1 = c(16, 11, 16, 18, 20, 19, 16, 18, 20, 15, 
          17, 19, 19, 19, 16, 19, 16, 15, 19, 19, 16, 18, 18, 19, 19, 18, 
          20, 18, 19, 19, 19, 19, 17, 19, 17, 16, 18, 19, 16, 18, 17, 19, 
          19, 20, 17, 16, 18, 16, 15, 19, 19, 17, 20, 18, 16, 19, 19, 15, 
          17, 17, 19, 19, 16, 17, 18, 19, 17, 19, 17, 15, 19, 16, 17
          )
        , A2 = c(8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 
              8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 
              8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 
              8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
              )
      , A3 = c(33, 34, 38, 36, 36, 34, 41, 36, 40, 38, 38, 41, 38, 34, 33, 36, 
            41, 40, 41, 38, 41, 33, 40, 38, 40, 38, 41, 41, 40, 41, 40, 
            38, 34, 40, 36, 41, 40, 40, 33, 38, 36, 41, 40, 40, 28, 41, 
            40, 41, 33, 41, 36, 36, 40, 34, 41, 41, 38, 38, 41, 38, 41, 
            41, 36, 40, 38, 38, 40, 41, 38, 22, 36, 34, 38
            )
        , Total = c(57, 53, 62, 62, 64, 61, 65, 62, 68, 61, 63, 68, 65, 61, 57, 63, 
        65, 63, 68, 65, 65, 59, 66, 65, 67, 64, 69, 67, 67, 68, 67, 
        65, 59, 67, 61, 65, 66, 67, 57, 64, 61, 68, 67, 68, 53, 65, 
        66, 65, 56, 68, 63, 61, 68, 60, 65, 68, 65, 61, 66, 63, 68, 
        68, 60, 65, 64, 65, 65, 68, 63, 45, 63, 58, 63
        )
    )
  , class = "data.frame"
  , row.names = c(NA, -73L)
  )

tb1 %>% 
  filter(Total == 63)
#>   A1 A2 A3 Total
#> 1 17  8 38    63
#> 2 19  8 36    63
#> 3 15  8 40    63
#> 4 19  8 36    63
#> 5 17  8 38    63
#> 6 17  8 38    63
#> 7 19  8 36    63
#> 8 17  8 38    63

tb2 <- 
  tb1 %>% 
  mutate(A4 = replace(A3, Total == 63, A3-1)) %>% 
  mutate(Total = A1 + A2 + A3)
#> Warning: Problem with `mutate()` input `A4`.
#> x number of items to replace is not a multiple of replacement length
#> ℹ Input `A4` is `replace(A3, Total == 63, A3 - 1)`.

tb2 %>% 
  filter(Total == 62)
#>   A1 A2 A3 Total
#> 1 16  8 38    62
#> 2 18  8 36    62
#> 3 18  8 36    62


Solution 1:[1]

You are better using ifelse here :

library(dplyr)
tb1 %>% mutate(A4 = ifelse(Total == 63, A3 -1, A3))

As far as why replace does not work if you check the source code of replace :

replace
function (x, list, values) 
{
   x[list] <- values
   x
}

It assigns values to x after subsetting for list.

When you use :

tb1 %>% mutate(A4 = replace(A3, Total == 63, A3-1))

your values is of length length(tb1$A3) but list is of length sum(tb1$Total == 63) which do not match hence you get the warning of number of items to replace is not a multiple of replacement length, since it tries recycling those values but still the length is unequal.

If you want to make replace work you can try :

tb1 %>%  mutate(A4 = replace(A3, Total == 63, A3[Total == 63] -1))

but again as I mentioned it is easier to just use ifelse here.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Ronak Shah