6
\$\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\$
5
  • \$\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, 2016 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, 2016 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, 2016 at 10:54
  • \$\begingroup\$ Ah, I see, I reacted too fast. \$\endgroup\$
    – Mast
    Dec 31, 2016 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, 2016 at 10:58

5 Answers 5

9
\$\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
  • 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\$ Dec 31, 2016 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, 2016 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, 2016 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, 2017 at 20:54
  • \$\begingroup\$ @Graipher good point, and other implementations may vary \$\endgroup\$
    – jonrsharpe
    Jan 1, 2017 at 22:11
11
\$\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
  • 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, 2017 at 17:35
-1
\$\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\$
2
  • 1
    \$\begingroup\$ Welcome to Code Review! Please (re-) read The help center page How do I write a good answer?. Note it states: "Every answer must make at least one insightful observation about the code in the question." When you have earned enough reputation you can leave a comment on the other answers. \$\endgroup\$ Sep 6, 2019 at 18:27
  • \$\begingroup\$ As your code shows you can use [::-1] to reverse a string. The OP already knew you can, and I thought saying something people already know wasn't very helpful. However you can use EX_STR.translate(trans)[::-1]. \$\endgroup\$
    – Peilonrayz
    Apr 29, 2021 at 14:34
-2
\$\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\$
0
-5
\$\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\$
1
  • 7
    \$\begingroup\$ That is not terribly efficient, as it creates multiple intermediate strings, and is also rather inelegant. \$\endgroup\$
    – jonrsharpe
    Dec 31, 2016 at 12:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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