5
\$\begingroup\$

I have a function in python where I want the reverse complement of a DNA string.

This is what I have now:

def inversComplement(input):
    output = ''
    for letter in input:
        letter = letter.upper()

        if letter == 'A':
            output += 'T'
        elif letter == 'T':
            output += 'A'
        elif letter == 'G':
            output += 'C'
        else:
            output += 'G'

    return(output[::-1])

I was thinking of using the string.replace() method. But the problem was that if I use this the T-> A would be changed afterwards with the A -> T replacement

    string.replace('A','T').replace('T','A').replace('G','C').replace('C','G')
\$\endgroup\$
  • \$\begingroup\$ Welcome to Code Review! This question does not match what this site is about. Code Review is about improving existing, working code. Code Review is not the site to ask for help in fixing or changing what your code does. Once the code does what you want, we would love to help you do the same thing in a cleaner way! Please see our help center for more information. \$\endgroup\$ – Mast Dec 31 '16 at 10:53
  • \$\begingroup\$ You're currently iterating 4 times over the string. If you only iterate once, you're set. \$\endgroup\$ – Mast Dec 31 '16 at 10:54
  • 1
    \$\begingroup\$ Well my code works actually :) (the one with the if statements). I was just wondering if it was possible to use the string.replace() method to make it better. \$\endgroup\$ – Roelland Dec 31 '16 at 10:54
  • \$\begingroup\$ Ah, I see, I reacted too fast. \$\endgroup\$ – Mast Dec 31 '16 at 10:56
  • \$\begingroup\$ I know it can be done using regex substitution in Ruby like this, can't imagine it's much different in Python. \$\endgroup\$ – Mast Dec 31 '16 at 10:58
8
\$\begingroup\$

Yesterday I came across this presentation on YouTube with this solution using list comprehension and the join method:

def reverse_complement(dna):
    complement = {'A': 'T', 'C': 'G', 'G': 'C', 'T': 'A'}
    return ''.join([complement[base] for base in dna[::-1]])
\$\endgroup\$
  • 5
    \$\begingroup\$ You should use a generator expression instead of a list comprehension: ''.join(complement[base] for base in dna[::-1]). Also, if you could explicitly state how this solution is superior to the original code, that would make this a better answer. (It looks nicer, but how does the performance compare, for example?) \$\endgroup\$ – 200_success Dec 31 '16 at 11:38
  • 1
    \$\begingroup\$ @200_success the first thing str.join does when given an iterator is consume it into a list: stackoverflow.com/a/34822788/3001761 so there's no benefit vs the list comprehension. \$\endgroup\$ – jonrsharpe Dec 31 '16 at 11:52
  • \$\begingroup\$ I feel there should be a way with a simpler dict: complement = {'A': 'T', 'C': 'G'} This would more closely match how someone would write down the complementation rules. \$\endgroup\$ – Ryan Mills Dec 31 '16 at 14:42
  • 2
    \$\begingroup\$ @jonrsharpe There is the benefit that if CPython gets a faster implementation you immediately take advantage of it, whereas the explicit list cast does not. \$\endgroup\$ – Graipher Jan 1 '17 at 20:54
  • \$\begingroup\$ @Graipher good point, and other implementations may vary \$\endgroup\$ – jonrsharpe Jan 1 '17 at 22:11
9
\$\begingroup\$

You can do this with str.translate, which was changed slightly in Python 3. All you need to do is pass it a translation table, and it'll do what your above code is doing. However to build the translation table is hard in Python 2 and requires the helper function string.maketrans, which was changed to str.maketrans in Python 3.

But here is how you'd use it in Python 2:

>>> import string
>>> trans = string.maketrans('ATGC', 'TACG')
>>> 'ATTAGCC'.translate(trans)
'TAATCGG'

And here is how you can use it in Python 3:

>>> trans = str.maketrans('ATGC', 'TACG')
>>> 'ATTAGCC'.translate(trans)
'TAATCGG'
>>> trans = {ord('A'): 'T', ord('T'): 'A', ord('G'): 'C', ord('C'): 'G'}
>>> 'ATTAGCC'.translate(trans)
'TAATCGG'

You can re-implement this yourself, but the above is written in C, and so should out perform any Python code.

\$\endgroup\$
  • 1
    \$\begingroup\$ I like this solution more than the one I found on YouTube. (@Roelland you might consider to change the accepted answer to this one) \$\endgroup\$ – Jan Kuiken Jan 1 '17 at 17:35
0
\$\begingroup\$

A quick caution on the solutions proposed in other answers. The translation table approach doesn't work because it's answering a slightly different question.

The REVERSE complement of ACGT is ACGT not TGCA. Because when you do a reverse complement you have to start by complementing item -1 then -2 etc. Rather than item 0 then 1 etc.

If you run proposed solutions you'll see they produce different answers.

>>> EX_STR="ACGT"

>>> ### Option 1 Correct
>>> def reverse_complement(dna): 
>>>     complement = {'A': 'T', 'C': 'G', 'G': 'C', 'T': 'A'} 
>>>     return ''.join([complement[base] for base in dna[::-1]])
>>> print reverse_complement(EX_STR)
ACGT

>>> ### Option 2 Incorrect
>>> import string 
>>> trans = string.maketrans('ATGC', 'TACG')
>>> print EX_STR.translate(trans)
TGCA
\$\endgroup\$
-1
\$\begingroup\$

You can in fact use this method:

string.replace('A','T').replace('T','A').replace('G','C').replace('C','G')

You just need to replace the uppercase letters by lowercase letters and make them uppercase again after that:

a=string.replace('A','t').replace('T','a').replace('G','c').replace('C','g')
a.upper()

It's maybe not the best way but it's easy and it works.

\$\endgroup\$
-4
\$\begingroup\$

I have found a solution with the string.replace() method!

def inversComplement(input):
    return(input.upper().replace('A', 'temp').replace('T', 'A').replace('temp', 'T').replace('G', 'temp').replace('C','G').replace('temp','C')[::-1])
\$\endgroup\$
  • 6
    \$\begingroup\$ That is not terribly efficient, as it creates multiple intermediate strings, and is also rather inelegant. \$\endgroup\$ – jonrsharpe Dec 31 '16 at 12:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.