simplifying DNA/ternary encryption process

Viewed 26

I wanted to simplify a DNA/ternary encryption algorithm. On one hand I have the encrypiton process where I transform a set of base-3 numbers triads (0,1,2) to DNA nucleotides (A,T,C,G). And the de-encryption process that just go the other way around.

The main point of this encryption is to not have two identical consecutive nucleotides together. So if even if the original vector is c(0,0,0,0) the DNA encrypted won´t be c("A","A","A","A"). This is achived by following the tables's algorithm. enter image description here

Where to deal with the first triad/nucleotide you assume the previus nucleotide is "A". Lest's see a couple of examples.

  • Encrypting: c(0,0,0,0) -> c("C","G","T","A")
  • De-encrypting c("T","A","C","G") -> c(2,0,0,0)

This is how I have automated the processes:

Encryption

> S4
 [1] "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "2" "0" "2" "1" "0" "2" "1" "2"
[31] "1" "1" "0" "1" "0" "1" "1" "0" "0" "0" "1" "0" "1" "2" "1" "0" "0" "0" "0" "0"

S5 <-c(1:length(S4))

for (i in 1:length(S4)) { 
  
  if (i == 1 ) {
    
    if (S4[1] == "0") {
      
      S5[1] <- "C"  
    } 
    
    if (S4[1] == "1") {
      
      S5[1] <- "G"
     
    }
    if (S4[1] == "2") {
      
      S5[1] <- "T"         
    }
  }
  if (i != 1) {
    
    if (S5[i-1] == "A" && S4[i] == "0") {
      
      S5[i] <- "C"
      
    }
    if (S5[i-1] == "A" && S4[i] == "1") {
      
      S5[i] <- "G"
      
    }
    if (S5[i-1] == "A" && S4[i] == "2") {
      
      S5[i] <- "T"
      
    }
    if (S5[i-1] == "C" && S4[i] == "0") {
      
      S5[i] <- "G"
      
    }
    if (S5[i-1] == "C" && S4[i] == "1") {
      
      S5[i] <- "T"
      
    }
    if (S5[i-1] == "C" && S4[i] == "2") {
      
      S5[i] <- "A"
      
    }
    if (S5[i-1] == "G" && S4[i] == "0") {
      
      S5[i] <- "T"
      
    }
    if (S5[i-1] == "G" && S4[i] == "1") {
      
      S5[i] <- "A"
      
    }
    if (S5[i-1] == "G" && S4[i] == "2") {
      
      S5[i] <- "C"
      
    }
    if (S5[i-1] == "T" && S4[i] == "0") {
      
      S5[i] <- "A"
      
    }
    if (S5[i-1] == "T" && S4[i] == "1") {
      
      S5[i] <- "C"
      
    }
    if (S5[i-1] == "T" && S4[i] == "2") {
      
      S5[i] <- "G"
      
    }
  }     
}


 > S5
[1] "CGTACGTACGTACGTACGTACGCGCTATCAGACTAGACGTCGATCGTACG"

De-encryption

adn <- S5

adn <- unlist(strsplit(adn, split = ""))
adn
s4 <- c()

for (i in 1:length(adn)) { #Loops to transform DNA sequence to terminary according to manual
  if (i != 1) {
    
    if (adn[i-1] == "A" && adn[i] == "C") {
      
      s4[i] <- 0
    
    }
    if (adn[i-1] == "A" && adn[i] == "G") {
      
      s4[i] <- 1
      
    }
    if (adn[i-1] == "A" && adn[i] == "T") {
      
      s4[i] <- 2
      
    }
    if (adn[i-1] == "C" && adn[i] == "G") {
      
      s4[i] <- 0
      
    }
    if (adn[i-1] == "C" && adn[i] == "T") {
      
      s4[i] <- 1
      
    }
    if (adn[i-1] == "C" && adn[i] == "A") {
      
      s4[i] <- 2
      
    }
    if (adn[i-1] == "G" && adn[i] == "T") {
      
      s4[i] <- 0
      
    }
    if (adn[i-1] == "G" && adn[i] == "A") {
      
      s4[i] <- 1
      
    }
    if (adn[i-1] == "G" && adn[i] == "C") {
      
      s4[i] <- 2
      
    }
    if (adn[i-1] == "T" && adn[i] == "A") {
      
      s4[i] <- 0
      
    }
    if (adn[i-1] == "T" && adn[i] == "C") {
      
      s4[i] <- 1
      
    }
    if (adn[i-1] == "T" && adn[i] == "G") {
      
      s4[i] <- 2
      
    }
  }
  
  if (i == 1 ) {
    
    if (adn[1] == "C") {
      
      s4[1] <- 0
      
      
    } 
    
    if (adn[1] == "G") {
      
      s4[1] <- 1
      
      
    }
    
    if (adn[1] == "T") {
      
      s4[1] <- 2
      
    }
  }
}
    > s4
 [1] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 2 1 0 2 1 2 1 1 0 1 0 1 1 0 0 0 1 0 1 2 1 0 0 0 0 0

Question

How do I make this couple of steps more elegant?

1 Answers

We can simplify this by making a data.frame containing the encodings, which lets us get access them without testing for each combination:

enc <- data.frame(A=c('C','G','T'),
                  C=c('G','T','A'),
                  G=c('T','T','C'),
                  T=c('A','C','G'),
                  row.names = c(0,1,2))

The decode function is simple, since we have all the information we need upfront. We just go through the string, getting the current base, the previous base (defaulting to 'A' if absent), then using them to look up where in the table matches up with those values, returning the rowname which has the value for the trit:

decode <- function(d) {
    sapply(seq_along(d), function(i) {
        base = d[i]
        prev = ifelse(length(d[i-1]) == 0, 'A', d[i-1])
        rownames(enc)[which(enc[[prev]] == base)]
    })
}

decode(c('T', 'A', 'C', 'G'))
[1] 2 0 0 0

Encoding is a bit harder since we need to look at the previously encoded values, but it's pretty much the same. The only difference is that we need to use a for loop and store each value in a list as we encode them, so we can look back and find the previous trit.

encode <- function(e) {
    res <- list()
    for (i in seq_along(e)) {
        trit = e[i]
        prev = ifelse(length(res[i-1]) == 0, 'A', res[[i-1]])
        res[[i]] <- enc[as.character(trit), prev] # Select row by name, not index
    }
    return(unlist(res)) # unlist to return vector, not list
}

encode(c(0,0,0,0))
[1] "C" "G" "T" "A"
Related