전사/번역을 Biopython 없이 해보자

반응형

이걸 왜 하냐... Biopython 깔면 장땡 아니냐... 변환해주는 툴 있지 않냐... 하시는데 변환해주는 툴은 인터넷이 안되면 못 쓰고, Biopython은 VScode에서 안된다. 일단 리눅스에서는 spyder랑 pycharm에서만 정상적으로 인식했다.


전사

DNA를 인트론 자르고 mRNA로 만드는 과정이다. 그리고 RNA가 DNA와 다른 점은 티민 대신 우라실(U)이 들어간다는 점. 그래서 ATGC->AUGC로 바꾸기만 하면 된다.

 

def transcription(text) :
    pass

우리는 찍어낸다 함수. 원래는 파일을 따로 하려고 했는데 아니 이게… 씁… 전사가 너무 간단함.

 

def transcription(text) :
    text = text.replace('T','U')
    return text

sequence = input('DNA 시퀀스를 입력해주세요: ')
sequence = sequence.upper()

print(transcription(sequence))

위에도 썼지만 전사는 T(티민)를 U(우라실)로 바꾸기만 하면 된다. 진짜로 그게 다다. 문제는 번역이지…

 

번역

번역이 왜 문제임? 을 설명하려면 이 표가 필요하다.

 

이건 코돈 테이블이라는 거다. 당신이 이 글을 읽는 시간에도 리보솜에서 mRNA를 해독하고 아미노산 호출해서 단백질 만들 때 이 테이블대로 만들어서 지지고 볶고 PTM까지 조지는 과정을 거치는데... 으악 이게 뭐예요? 왼쪽에 있는 세 글자가 mRNA의 염기 세 개다. 그러니까 구아닌 시토신 시토신은 알라닌이라는 아미노산을 지정한다 이 얘기. 이건 생물종별로 코돈이 다르긴 한데 보통은 사람 코돈으로 하니까 그것까진 걱정 안 하셔도 됨.

 

여기서 뭐가 문제인지 깨달은 분도 계실 것이다. 그렇다. 여기서 문제는 전사와 달리 번역을 할 때는 알파벳 세 개를 찝어다가 저 표에서 맞는 아미노산을 골라야 한다는 거다. 한 아미노산에 대해서 여러가지 코돈이 대응하는 경우는 있지만, 번역하는 데 있어서 그건 문제가 안된다. 우리는 염기를 3개씩 묶어서 할 거지 아미노산으로 염기 찾는 걸 하는 게 아니거든…

 

그 외에도 고려할 게 몇 개 있는데, 그건 차차 설명하도록 하자. 기능 구현하는데도 피똥 쌀 예정이다.

 

def translation(text) :
    pass

우리는 또 찍어낸다 함수를.

 

codon_table = {
    # U
    "UUU":"F", "UUC":"F", "UUA":"L", "UUG":"L", 
    "UCU":"S", "UCC":"S", "UCA":"S", "UCG":"S",
    "UAU":"Y", "UAC":"Y", "UAA":"*", "UAG":"*",
    "UGU":"C", "UGC":"C", "UGA":"*", "UGG":"W",

    # C
    "CUU":"L", "CUC":"L", "CUA":"L", "CUG":"L", 
    "CCU":"P", "CCC":"P", "CCA":"P", "CCG":"P", 
    "CAU":"H", "CAC":"H", "CAA":"Q", "CAG":"Q", 
    "CGU":"R", "CGC":"R", "CGA":"R", "CGG":"R", 

    # A
    "AUU":"I", "AUC":"I", "AUA":"I", "AUG":"M", 
    "ACU":"T", "ACC":"T", "ACA":"T", "ACG":"T", 
    "AAU":"N", "AAC":"N", "AAA":"K", "AAG":"K", 
    "AGU":"S", "AGC":"S", "AGA":"R", "AGG":"R", 

    # G
    "GUU":"V", "GUC":"V", "GUA":"V", "GUG":"V", 
    "GCU":"A", "GCC":"A", "GCA":"A", "GCG":"A", 
    "GAU":"D", "GAC":"D", "GAA":"E", "GAG":"E", 
    "GGU":"G", "GGC":"G", "GGA":"G", "GGG":"G"
}

gpt한테 물어봤더니 코돈 테이블을 딕셔너리로 만든 다음 거기서 뒤져서 대체하게 하란다. 일회성으로 돌릴거면 함수 안에 넣어도 되는데 저거 한번 돌리고 땡 아니면 걍 전역 하라고 함… 전역변수 하라는 얘기는 함수 밖으로 빼라는 얘기다. 왜 그렇게 하냐고? 전역변수면 한 번만 부르면 되는데, 함수 안에 때려박으면 함수 돌릴때마다 쟤를 불러야 한다 이거지.

 

def translation(text) :
    text = text.upper()
    protein = []
    for i in range(0, len(text) - 2, 3):
        codon = text[i:i+3]
        amino_acid = codon_table.get(codon, '???')
        protein.append(amino_acid)
    return "".join(protein)

그러니까 착한 코더분들은 툴 찾아서 쓰십쇼… 일단 저 ???가 굳이 필요한지는 모르겠는게, 연구자들이 저런거 돌리는 시퀀스는 이미 DB에 있는거다. 그러니까 없는 코돈이 없다. for문 안에 0, len(text) - 2, 3으로 되어있는 이유는 0부터 시퀀스 길이-2까지 3개만인데…보통 시퀀스 3배수로 하지 않나? 라고 할 수 있지만 PCR 산물은 3배수 아닐수도 있다.

 

된 거 맞다. DNA를 입력해서 RNA로 전사하고 그걸로 번역까지 해야 한다.

 

아직 안 끝났다! 

중요한건 우리는 이제 막 번역 로직을 구현했을 뿐이고, 실제로 생물학 연구자들이 활용하는 두 가지에 대해서는 구현이 안 됐다. 그 두 가지가 뭐냐...

 

첫번째, 모든 번역의 시작점은 AUG(메티오닌)이다. 그러니까 DNA 시퀀스에서는 몇 번째에 존재하던 상관없이 AUG가 시작점이다. GAATTGCTAUG 뭐 이런 시퀀스가 있으면 GAATTGCT[AUG] <<여기 대괄호 친 AUG가 시작점이다.

 

두번째, 종결코돈이 나오면 더 이상 번역 안 하고 끝낸다. 위에 돌린 거 보면 번역 결과가 GSSRVPRVLSK*NELPYIEEGSCEG*WDCASSLTSVEIPDRPNACPRSIQDITSIHLL*RRGWNVFFFHDAPRGWGSIFGTTVGRGIFNDGLSFIAMMAFVGATFLFHYLHNKVTDSWAMESEEVSGYYPLLKSLNCPLVF*DCIFDIFGVDKCVVLHHVD
로 나오는데, 원래 번역대로라면 GSSRVPRVLSK에서 끝났어야 맞다.

 

def translation(text) :
    text = text.upper()
    protein = []
    for i in range(0, len(text) - 2, 3):
        codon = text[i:i+3]
        amino_acid = codon_table.get(codon, '???')
        protein.append(amino_acid)
        if amino_acid == "*":
            break
    return "".join(protein)

두번째 부분에 대해서는 if문 하나 추가하면 땡이다. 내가 위에 전역변수 선언한 표에 보면 종결코돈이 *로 되어있다. 그러니까 테이블 돌다가 * 나오면 끝내셈 하면 된다. 종결코돈은 툴 바이 툴인데 마침표로 표시하는데도 있음. 

 

def translation(text) :
    text = text.upper()
    start = text.find("AUG")

    if start == -1:
        return "No start point(AUG)"

    protein = []
    for i in range(start, len(text) - 2, 3):
        codon = text[i:i+3]
        amino_acid = codon_table.get(codon, '???')
        protein.append(amino_acid)
        if amino_acid == "*":
            break
    return "".join(protein)

find함수로 AUG를 찾은 다음 거기서부터 번역을 시작하면 장땡이다. 이게 또 피쌸돌렸거나 하면 없을 수도 있기때문에 if문 하나 때려박아줌... 그런데! 여기서 또 중요한 게 하나 있다. 저 기능이 보통 옵션으로 제공된다. 그러니까 AUG를 안 찾고 번역하는 함수도 필요하긴 하다는 것.

 

그래서 AUG로 시작하는 거, 아닌거 둘 다 했다. (함수 따로 빼버림)

 

결론: 착한 연구자 여러분들은 이런거 구현할 시간에 그냥 툴 쓰십시오

반응형