Flipping binary figures in a string vector without looping in R

Viewed 37

I have a vector with equal sized 0/1 elements, dna. And a similar vector with same size, flip. If the flip = 1, I want to flip the corresponding figure in the dna vector. So 0 would change to 1 and 1 would change to 0. And without looping to make it fast. My real dataset has a lot of data. Below is some sample data:

#input
dna  = c('0101010100', '1010101010', '1010101011')
flip = c('0100000001', '0000000000', '1000000000')

#requested answer
dna_flipped  = c('0001010101', '1010101010', '0010101011')
#first element: second and 10th character is flipped
#second element: nothing is changed
#third element: first character is changed

#try loop solution
flip_split = lapply(strsplit(flip, ''), function(x) which(x == '1'))

for (i in 1:length(dna)){
  for(j in seq_along(flip_split[[i]])){
    k = flip_split[[i]][j]
    substring(dna[[i]],k,k) = as.character(abs(1 - as.integer(substring(dna[[i]],k,k)))) 
  }
}

How can this be done without a loop?

1 Answers

I think the logic you are describing is equivalent to a logical XOR. The difficult part is applying this to character strings. The following should work, and is st least vectorized per element so you don't need to iterate along individual characters:

unname(unlist(Map(function(a, b) {
  paste(as.numeric(xor(as.numeric(charToRaw(a)) - 48 == 1,
                       as.numeric(charToRaw(b)) - 48 == 1)), collapse = "")
  }, a = dna, b = flip)))
#> [1] "0001010101" "1010101010" "0010101011"

Or, perhaps more efficiently, as Ritchie Sacramento points out:

unname(unlist(Map(function(a, b) {
  rawToChar(as.raw(as.numeric(xor(as.numeric(charToRaw(a)) - 48 == 1,
                       as.numeric(charToRaw(b)) - 48 == 1)) + 48))
  }, a = dna, b = flip)))
#> [1] "0001010101" "1010101010" "0010101011"
Related