switch elements between columns based on the value of another column r

Viewed 43

I have a data frame like this:

D <- data.frame(V1=c(1,2,3,4), V2=c(6,7,8,9), V3=c(3,4,5,6), sign=c("+", "-", "-", "+"))

If the character in column "sign" is "-", I would like to switch values in columns V1 and V2

My desired output would be:

          V1 V2 V3 sign
        1  1  6  3    +
        2  7  2  4    -
        3  8  3  5    -
        4  4  9  6    +

I have tried for loops and if and else statements, but it is not working. Could you please tell me how I could achieve this?

1 Answers

Use mapply to loop through the 3 columns at once and assign the result to columns V1 and V2.

D <- data.frame(V1=c(1,2,3,4), V2=c(6,7,8,9), V3=c(3,4,5,6), sign=c("+", "-", "-", "+"))

D[c("V1", "V2")] <- t(mapply(\(x, y, s){
  if(s == "-") c(y, x) else c(x, y)
}, D$V1, D$V2, D$sign))
D
#>   V1 V2 V3 sign
#> 1  1  6  3    +
#> 2  7  2  4    -
#> 3  8  3  5    -
#> 4  4  9  6    +

Created on 2022-03-18 by the reprex package (v2.0.1)


Edit

Though the answer was already accepted, here is the code above as a function together with two other functions, in increasing order of speed. The original is the slowest, the 3rd is the fastest.

swapCols1 <- function(X, flag){
  X[] <- t(mapply(\(x, y, s){
    if(s) c(y, x) else c(x, y)
  }, X[[1]], X[[2]], flag))
  X
}

swapCols2 <- function(X, flag){
  s <- seq.int(nrow(X))
  i <- cbind(c(s, s), c(flag, !flag) + 1L)
  matrix(X[i], nrow = nrow(X))
}

swapCols3 <- function(X, flag){
  tmp <- X[[1]][flag]
  X[[1]][flag] <- X[[2]][flag]
  X[[2]][flag] <- tmp
  X
}
Related