본문 바로가기

파이썬 공공공

Cis-AB형의 유전 시뮬레이션 [Python]

Cis-AB형이란?

ABO식 혈액형은 세개의 대립유전자 A, B, O에 대한 복대립 유전이다. 당연히 정상적인 경우에 세개의 유전자가 같은 염색체 위에 있을 일은 없다. 하지만 Cis-AB형은 A,B 형을 나타내는 대립유전자가 같은 염색체 위에 존재하는 돌연변이다. Cis-AB형 유전자를 가진 사람은 먼저, A,B 유전자가 둘다 있고, 상동염색체에 있는 다른 유전자까지 고려하여 우열 관계에 맞게 표현형을 나타낸다. 예를 들면 Cis-AB/O 형인 사람은 AB형, Cis-AB/A인 사람은 AB형이다.

 

위와 같은 규칙으로 사람의 ABO식 혈액형 유전을 시뮬레이션하였다. 사실 Cis-AB형이 아니더라도 정상 혈액형을 가진 집단에 대해서도 유전을 시뮬레이션 할 수 있게 구현하였다.

시뮬레이션 코드 구현

먼저 라이브러리는 아래 두개를 사용하였다.

import random
import matplotlib.pyplot as plt

 

다음으로 사람 1개 개체를 나타내는 클래스를 구현하였다.

class person:
    def __init__(self, al1, al2):
        self.al1 = al1
        self.al2 = al2

        aN = False
        bN = False
        if al1=='CisAB' or al1=='A' or al2=='CisAB' or al2=='A':
            aN = True
        if al1=='CisAB' or al1=='B' or al2=='CisAB' or al2=='B':
            bN = True

        if aN and bN:
            self.phenotype = 'AB'
        elif aN:
            self.phenotype = 'A'
        elif bN:
            self.phenotype = 'B'
        else:
            self.phenotype = 'O'


    def trans(self):
        ttt = random.randint(0,1)
        if ttt==0:
            return self.al1;
        else:
            return self.al2

 

객체 속성 phenotype은 각 객체의 ABO식 혈액형 표현형을 나타낸다.
객체가 ABO식 혈액형 대립유전자 두개를 받으며 생성되면 __init__ 함수에서 A,B 대립유전자 여부를 확인한 후 표현형을 결정한다.
trans 메서드는 객체의 대립유전자를 자손으로 전달할 때 사용한다. 자신이 가진 대립유전자중 하나를 선택하여 반환하게 된다.

다음은 세대를 진행하는 stepGeneration 함수이다.

 

def stepGeneration(nowGen):
    nextGen = []

    random.shuffle(nowGen);

    for i in range(0,len(nowGen)//2*2,2):
        for j in range(3):#자식 수 설정
            baby = person(nowGen[i].trans(),nowGen[i+1].trans())
            nextGen.append(baby)

    phenoCnt = [0,0,0,0]#AB, A, B, O

    for child in nextGen:
        if child.phenotype == 'AB':
            phenoCnt[0] += 1
        elif child.phenotype == 'A':
            phenoCnt[1] += 1
        elif child.phenotype =='B':
            phenoCnt[2] += 1
        else:
            phenoCnt[3] += 1


    return nextGen, phenoCnt

 

본 함수의 입력 매개변수인 nowGen은 앞서 선언한 person 객체의 리스트이다.
먼저 현재 세대 객체가 담긴 리스트를 랜덤한 순서로 뒤섞고 순차적으로 두명씩 교배시킨다. 이때 성별은 고려하지 않는다.
또한 세대의 구성원 수가 홀수인 경우에 리스트 마지막 한명은 쓸쓸하게 버려진다...

 

자식수는 위 코드에서는 3으로 설정하였다. 더욱 현실적인 시뮬레이션을 위해서는 자식 수 또한 랜덤하게 할 수 있을것이다.


다음으로 phenoCnt리스트는 현재 세대의 표현형 수를 세는 리스트이다. 0번 인덱스부터 AB, A, B ,O를 나타낸다. 그 다음의다음 for문으로 표현형 수를 센다.
최종적으로 stepGeneration함수는 다음 세대 객체(개체)들의 리스트와 표현형의 빈도수 리스트를 반환하게 된다.

시뮬레이션 코드 구현은 이상 끝이다. 그치만 실험해보며 phenoCnt 리스트를 예쁘게 출력하기 편하려고 함수 하나만 더 선언하였다.

 

def phenoPlot(pheno):
    plt.figure(figsize=(5,4))
    bar = plt.bar(['AB','A','B','O'], pheno,color = 'red')

    for rect in bar:
        height = rect.get_height()
        plt.text(rect.get_x() + rect.get_width()/2.0, height, '%d' % height, ha='center', va='bottom', size = 12)
    plt.show()

시뮬레이션 해보기

먼저 조상집단을 설정해야한다. 임의의 집단을 설정해도 되지만, 나는 가능하면 모든 표현형이 나타나면 좋겠어서 아래와같이 두개의 조상집단을 설정했다. 두 집단으로부터 시작되는 유전을 비교해볼 것이다.

ancestorCis = [
            person('CisAB','O'),
            person('A','O'),
            person('B','O'),
            person('O','O')
            ]

ancestorDef = [
            person('A','B'),
            person('A','O'),
            person('B','O'),
            person('O','O')
            ]

 

ancestorCis는 Cis-AB형이 포함된 조상집단, ancestorDef는 정상인 조상집단이다.

이제 시뮬레이션 해보자.

gen = ancestorDef
history = []


for i in range(0,20): #20세대, 500년
    history.append(pheno)
    gen, pheno = stepGeneration(gen)

phenoPlot(pheno)

 

위 코드를 통해 정상 조상집단이 20세대동안 유전되었을 때, 최종적인 표현형은 어떻게 나타나는지 볼 수있다.
20을 다른 숫자로 바꾸어 원하는 세대만큼 시뮬레이션 할 수 있다.
gen = ancestorCis로 바꾼다면 Cis 포함 집단의 유전을 시뮬레이션 할 수 있다. 아마 이때가 특정 표현형이 나타나는 개체수가 0인 경우가 많을 것이다.

결론

이렇게 구현한 코드로 여러가지 시뮬레이션을 해 볼수 있다. 방금 언급한 마지막 세대에 특정 표현형이 나타나는 개체수가 0인 경우는 Cis 포함 조상집단의 유전에서 정상 조상집단의 유전에 비해 약 3배가까이 많다. 반복실험 결과 각각 48% 16%를 크게 벗어나지 않는 수치로 나온다. (가계도 그려보면 당연할 듯 싶은데 아무튼 시뮬레이션 상 그렇다.)

 

실험을 통해 이외에도 하디-바인베르크 법칙 (대립유전자의 빈도는 세대가 거듭하여도 변하지 않는다는 법칙) 등을 확인하거나 반박해볼 수 있을것이고, 최종 세대의 표현형이 3세대 정도의 표현형과 얼마나 일치하는 지 확인하는 등 (창시자 효과) 여러 탐구를 해볼 수 있을것이다.

'파이썬 공공공' 카테고리의 다른 글

RSA 알고리즘 뚫기 [Python]  (1) 2024.08.26