Skip to content

KOBIC-Edu33 #
Find similar titles

제33 KOBIC 차세대 생명정보학 교육 워크샵 데이터 과학을 위한 파이썬 기초

개요 #

  • 일시: 2016-08-24 ~ 2016-08-26
  • 장소: (주)인실리코젠 본사 人CoFLEX
  • 내용: 프로그래밍 언어 파이썬 집중 단기 학습을 통해 생물정보 데이터 분석 실무 능력 습득
  • URL: http://kobicedu.labkm.net

강의 특징 #

  • 이론과 실습 병행 (강의 1시간, 실습 1시간 - 하루 3회)
  • 첫째날 서버에 원격 접속 (putty)
  • 둘째날 개인 PC에 Jupyter 설치하여 실습
  • 모든 실습은 페어프로그래밍으로 진행 (하루 1회 페어 변경)

교육 환경 #

원격 실습 서버 접속 방법 #

Windows #

  1. Putty 프로그램 설치: Putty download page에서 Windows용 MSI 설치 파일 putty-version-installer.msi 다운로드 및 실행
  2. Putty 프로그램 실행 후, 설정창에서 다음 입력 후 저장
    1. Host Name: edu.kobic.re.kr
    2. Port: 2022
    3. Saved Sessions: kobic33
  3. kobic33 선택 후 Open 클릭하여 접속
  4. login as: 부분에서 부여받은 아이디 입력
  5. `s password: 부분에서 비밀번호 입력
  6. 접속 성공!

Linux/OS X #

Term 프로그램 구동 후, 다음처럼 입력하여 접속 (kobic23 사용자라면,)

$ ssh kobic23@edu.kobic.re.kr -p2022

Jupyter 설치 방법 #

Windows #

https://www.continuum.io/downloads#_windows에서 윈도우용 아나콘다 다운로드 한 후, 명령프롬프트에서 다음을 실행

$conda install jupyter
$jupyter notebook

Linux/OS X #

터미널에서 다음을 실행

# homebrew 설치
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

# pyhton3 설치
$ brew install python3

# jupyter 설치
$ pip3 install jupyter

# 라이브러리 설치
$ pip install matplotlib
$ pip install pandas

# jupyter 실행
$ jupyter notebook

교육 내용 #

8월 24일 (1일차) #

파이썬 개요 #

참고 정보

기본 자료형 #

연습문제 1. 파이썬 명령행 환경에서 a, b 변수가 다음과 같을 때,

#!python    
$ python3
>>> a = 14
>>> b = 3

다음을 계산하세요.

  1. a를 b로 나눈 몫과 나머지
  2. a의 b 제곱
  3. a x log(b) (밑이 3)
  4. a를 실수부, b를 허수부로 하는 복소수의 켤레복소수
  5. a를 2진수로
  6. 16진수 123ab를 10진수로 바꾸면?

정답 1

#!python
>>> a // b    # 몫
4
>>> a % b     # 나머지
2
>>> divmod(a, b)
(4, 2)
>>>
>>> import math
>>> a * math.log(b, 3)
14.0
>>>
>>> a + b * 1j
(14+3j)
>>> (a + b * 1j).conjugate()
(14-3j)
>>> bin(a)
'0b1110'
>>> 0x123ab
74667
>>>

연습문제 2. 파이썬 명령행 환경에서 my_sentence 변수가 다음과 같을 때,

#!python
$ python3
>>> my_sentence = "Hello my world!"

다음을 출력하는 방법은?

  1. Hello
  2. my world
  3. m
  4. !

정답 2

#!python
>>> my_sentence = "Hello my world!"
>>> my_sentence[:5]
'Hello'
>>> my_sentence[7:-1]
'y world'
>>> my_sentence[6:-1]
'my world'
>>> my_sentence[5]
' '
>>> my_sentence[6]
'm'
>>> my_sentence[-1]
'!'

연습문제 3. 파이썬 명령행 환경에서 my_list 변수가 다음과 같을 때,

#!python
$ python3
>>> my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

다음을 출력하는 방법은?

  1. 처음부터 중간까지만
  2. 짝수자리만
  3. 3의 배수자리만

연습문제 4. 다음처럼 두 집합의 내용(공백으로 분리)을 입력받아, 합집합, 교집합, 차집합을 출력하는 프로그램을 작성하시오.

#!python
$ python3 set_operation.py
Set A: 1 2 3 4 5
Set B: 5 6 7 8 9
----
Union --> 1 2 3 4 5 6 7 8 9 
Intersection --> 5
Diference --> 1 2 3 4

(힌트)

  1. 사용자로부터 입력을 받는 함수는 input 입니다.
  2. 공백 포함 문자열을 공백으로 나눈 리스트로 변환하려면 string.split() 메쏘드를 이용합니다.

정답 4

#!python
a = input('Set A: ')
b = input('Set B: ')

set_a = set(a.split())
set_b = set(b.split())

print('Union -->', set_a | set_b)
print('Intersection -->', set_a & set_b)
print('Difference -->', set_a - set_b)

제어구문 #

연습문제 1. 다음을 출력하는 파이썬 코드를 작성하시오.

#!python
$ python3 p1.py
*
**
***
****
*****
$ python3 p2.py
*****
 ****
  ***
   **
    *

정답 1

#!python
for i in range(1, 6): 
    print(‘* * i)

for i in range(5, 0, -1):
    print(' ' * (5-i) + '*' * i)

연습문제 2. 다음을 출력하는 파이썬 코드를 작성하시오.

#!python
$ python3 times_table.py
2 x 1 = 2
2 x 2 = 4
2 x 3 = 6
...
9 x 8 = 72
9 x 8 = 81

정답 2

#!python
for x in range(2, 10):
    for y in range(1, 10):
        print(x, 'x', y, '=', x * y)

파일입출력 #

연습문제 3. 임의의 텍스트 파일내 단어의 출현 빈도를 조사하여 가장 많이 출현한 단어부터 정렬하여 출력하는 프로그램 (특수기호 제외, 소문자로 통일)

#!python
$ python3 word_frequency.py < input.txt
32 the
28 of
17 boy
…

정답 3

#!python
import sys

frequencies = {}
special_characters = ['"', "'", '%', '(', ')', ':', '=', ']', '[', ',']
for line in sys.stdin:
    for sc in special_characters:
        line = line.replace(sc, ' ')
    words = line.strip().split()
    for word in words:
        if word in frequencies:
            frequencies[word] += 1
        else:
            frequencies[word] = 2

for word, frequency in sorted(
        frequencies.items(), key=lambda x: x[1], reverse=True)[:20]:
    print(frequency, word)

연습문제 4. 임의의 FASTA 파일을 읽어, GC content를 출력 (하나의 레코드이며 서열은 한줄만 있음을 가정)

#!python
$ cat test.fasta
>gene1
AGCAGACGTCGAGCAAG
$ python gc_content.py < test.fasta
0.588

정답

#!python
import sys

title = next(sys.stdin)
sequence = next(sys.stdin)

A = sequence.count('A')
G = sequence.count('G')
C = sequence.count('C')
T = sequence.count('T')

gc_content = (G + C) / (A + G + C + T)
print(gc_content)

연습문제 5. 임의의 FASTA 파일을 읽어, GC content를 출력 (하나의 레코드이며 서열은 여러줄 있을 수 있음)

#!python
$ cat test.fasta
>gene1
AGCAGACGTCGAGCAAG
AGCAGACGTCGAGCAAG
$ 
$ python3 gc_content.py < test.fasta
0.588

정답

#!python
import sys

title = sys.stdin.readline()
sequences = []
for line in sys.stdin:
    sequences.append(line)

sequence = ''.join(sequences)

A = sequence.count('A')
G = sequence.count('G')
C = sequence.count('C')
T = sequence.count('T')

print((G + C) / (A + G + C + T))

연습문제 6. 임의의 FASTA 파일을 읽어, Reverse complement 서열을 출력

#!python
$ cat test.fasta
>gene1
AGCAGACGTCGAGCAAG
AGCAGACGTCGAGCAAG
$ 
$ python3 gc_content.py < test.fasta
CTTGCTCGACGTCTGCT
CTTGCTCGACGTCTGCT

모듈, 패키지, 예외처리 #

연습문제 1. 구구단 출력 함수 (원하는 단을 입력 받도록, 함수로 작성)

#!python
$ multi.py
Please insert an integer: 3
3 * 1 = 3
3 * 2 = 6

$ multi.py
Please insert an integer: 12
12 * 1 = 12
12 * 2 = 24

정답 1

#!python
def time_table(x):
    for y in range(1, 10):
        print('{} x {} = {}'.format(x, y, x*y))

n = input('Please insert an integer: ')
time_table(n)

연습문제 2. Euclidean distance 계산하기

>>> p1 = [1, 2, 3, 4]
>>> p2 = [5, 6, 7, 8]
>>> get_euclidean_distance(p1, p2)
8.0

정답 2

#!python
import math

def get_euclidean_distance(X, Y):
    s = 0   
    for i in range(len(X)):
        x = X[i]
        y = Y[i]
        s += (x - y) ** 2
    return math.sqrt(s)

if __name__ == '__main__':
    p1 = [1, 2, 3, 4, 5]
    p2 = [1, 4, 6, 8, 9]

    print(get_euclidean_distance(p1, p2))

연습문제 3. 임의 개수의 숫자들을 함수의 인수로 받아 평균값을 출력하는 함수

>>> mysum(1, 2, 3)
2.0
>>> mysum(2, 4, 5, 7, 8)
5.2

정답 3

#!python
def mysum(*args):
    return sum(args) / len(args)

연습문제 4. datetime 모듈을 사용하여 다음 질문에 답하세요

  1. 오늘은 2016년 8월 10일입니다. 1000일 후는 며칠?

위 계산을 하는 모듈을 별도로 만들고, import 하기

>>> import date_calculator
>>> date_calculator.date_from_today(1000)

정답 4

#!python
import datetime

def date_calculator(days):
    return datetime.date.today() + datetime.timedelta(days)

연습문제 5. 임의의 FASTA 파일을 읽어, GC content를 출력 (Single FASTA 형식, 만일 AGTC 이외 다른 문자가 있으면 자체 오류출력, A,G,T,C 합계가 전체 길이와 다르면 예외발생)

$ cat test.fasta
>gene1
CGCAGACGTCGAGCAAB
AGCGACAGTCAGTCAGA

$ python gc_content.py < test.fasta
>gene1
AssertionError: Sequence characters have to be A, T, C, G

8월 25일 (2일차) #

Matplolib 한글폰트 설치 #

윈도우에서

from matplotlib import font_manager, rc
font_name  = font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name()
rc('font', family=font_name)

맥에서

matplotlib.font_manager.get_fontconfig_fonts()
krfont = {'family': 'AppleGothic', 'weight': 'bold', 'size': 10}
matplotlib.rc('font', **krfont)

리눅스에서

matplotlib.font_manager.get_fontconfig_fonts()
krfont = {'family': 'UnDotum', 'weight': 'bold', 'size': 10}
matplotlib.rc('font', **krfont)

Rosalind 생물정보 문제 사례 #

연습문제 1. Counting DNA Nucleotides

#!python
$ cat seq.txt
AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC

$ python3 count.py
20 12 17 21

풀이 1

#!python
##함수 없이
input = open('seq.txt', 'r')
seq = input.read()
a_count = seq.count('A')
c_count = seq.count('C')
g_count = seq.count('G')
t_count = seq.count('T')

out = "{} {} {} {}".format(a_count, c_count, g_count, t_count)
print(out)

## 함수 사용
def count_seq(seq):
    a_count = seq.count('A')
    c_count = seq.count('C')
    g_count = seq.count('G')
    t_count = seq.count('U')

    out = "{} {} {} {}".format(a_count, c_count, g_count, t_count)
    return out

count(open(‘seq.txt’).read())

연습문제 2. Transcribing DNA into RNA

#!python
$ cat seq.txt
GATGGAACTTGACTACGTAAATT
$
$ python trans.py
GAUGGAACUUGACUACGUAAAUU

풀이 2

#!python
##함수 없이
cat trans.py

input = open(‘seq.txt’, ‘r’)
seq = input.read()
rna = seq.replace(’T’, ‘U’)
print(rna)

##함수 사용
def dna2rna(seq):
    rna_seq = seq.replace('T', 'U')
    return rna_seq

seq = open('seq.txt', 'r').read()
rna_seq = dna2rna(seq)
print(rna_seq)

연습문제 3. Complementing a Strand of DNA

#!python
$ cat seq.txt
AAAACCCGGT
$
$ python revc.py
ACCGGGTTTT

풀이 3

#!python
## 방법1
$ cat prob3_revc/revc.py
import sys

#filename = sys.argv[1]
filename = 'seq.txt'
seq = open(filename).read().strip()

dic = {'A': 'T',
          'T': 'A',
          'C': 'G',
          'G': 'C'}

new = ''
for s in seq:
    new += dic[s]
print(new.upper()[::-1])

## 방법 2
$ cat revc/revc2.py
import sys

filename = sys.argv[1]
seq = open(filename).read().strip()
seq = seq[::-1] #reverse seq

seq = seq.replace("A", "t")
seq = seq.replace("C", "g")
seq = seq.replace("T", "a")
seq = seq.replace("G", "c")
print(seq.upper())

연습문제 4. Identifying Unknown DNA Quickly

#!python
$ cat seq.txt
>Rosalind_6404
CCTGCGGAAGATCGGCACTAGAATAGCCAGAACCGTTTCTCTGAGGCTTCCGGCCTTCCC
TCCCACTAATAATTCTGAGG
>Rosalind_5959
CCATCGGTAGCGCATCCTTAGTCCAATTAAGTCCCTATCCAGGCGCTCCGCCGAAGGTCT
ATATCCATTTGTCAGCAGACACGC
>Rosalind_0808
CCACCCTCGTGGTATGGCTAGGCATTCAGGAACCGGAGAACGCTTCAGACCAGCCCGGAC
TGGGAACCTGCGGGCAGTAGGTGGAAT

$ python gc.py seq.txt
Rosalind_0808
0.6091954022988506

풀이 4

#!python
import sys

filename = sys.argv[1]
seqs = open(filename).read().strip().split('>')

max_name = ""
max_gc = 0
for seq in seqs:
    if not seq:
        continue
    name = seq.split('\n')[0]
    seq = ''.join(seq.split('\n')[1:])
    gc = (seq.count('G') + seq.count('C')) / len(seq) * 100
    if max_gc < gc:
        max_gc = gc
        max_name = name
print(max_name)
print(max_gc)

연습문제 5. Counting Point Mutation

#!python
$ cat seq.txt
GAGCCTACTAACGGGAT
CATCGTAATGACGGCCT
$
$ python hamm.py seq.txt
7

풀이 5

#!python
import sys

filename = sys.argv[1]
seqs = open(filename).read().strip().split('\n')

#for seq in zip(*seqs):
count = 0
for seq1, seq2 in zip(seqs[0], seqs[1]):
    if seq1 != seq2:
        count += 1
print(count)

데이터 과학 개요 #

Jupyter & pandas #

참고정보

예제 데이터

8월 26일 (3일차) #

Matplotlib #

예제 데이터 다운로드 한 후, DataFrame으로 읽어오기

#!python
import pandas as pd
data = pd.read_csv('weight.csv')

연습문제 1 국가별 샘플 수 bar plot으로 그리기 (figsize: 10x10)

#!python    
import matplotlib.pyplot as plt
%matplotlib inline
data = pd.read_csv('weight.csv')
data['Country'].value_counts().plot(kind='bar', figsize=(10,10))

연습문제 2 성별 샘플 수 pie plot으로 그리기 (figsize: 10x10, colors: blue, red)

#!python    
data['Sex'].value_counts().plot(
    kind='pie’,
    figsize=(10,10), 
    colors=['blue', 'red’]
)

연습문제 3 성별로 구분하여 체중으로 boxplot 그리기 (figsize: 20x10, color:gray)

#!python    
data['Weight'].hist(
    by=data['Sex'], 
    figsize=(20,10), 
    color=‘gray'
)

연습문제 4 국가별로 구분하여 체중으로 히스토그램 그리기 (range: 0~120, figzise:20x10, bins:20)

#!python    
data['Weight'].hist(
    bins=20, range=(0,120),
    by=data['Country'],
    figsize=(20,10)
)

연습문제 5 세 개의 서브플롯을 만들고 다음과 같이 구성

  • 첫번째 서브플롯: 국가별 체중 boxplot
  • 두번째 서브플롯: 체중 histogram (20 bins)
  • 세번째 서브플롯: 성별 pie chart
  • 서브플롯 간의 위아래 간격: 0.1
  • figsize: 5,20
  • 'ex5.png'로 저장

    #!python    
    import matplotlib.pyplot as plt
    %matplotlib inline
    
    data = pd.read_csv('weight.csv')
    
    # figure, subplot 생성
    fig, axes = plt.subplots(3,1, figsize=(5,20), )
    # 국가별 체충의 boxplot 그리기
    data.boxplot(column='Weight', by='Country', ax=axes[0])
    #  체중의 histogram 그리기
    data['Weight'].hist(ax=axes[1], color='gray', bins=20, range=(0,120))
    # 성별로 pie차트 그리기
    data['Sex'].value_counts().plot(ax=axes[2], kind='pie')
    plt.subplots_adjust(hspace=0.5)
    #ex5.png에 저장
    plt.savefig(‘ex5.png’)
    

유전변이 데이터(VCF) 분석 #

예제데이터

KOBIC 서버 vcf 파일 경로

#!python
df = pd.read_table('/BiO/home/kobic/Pfeiffer-quartet.vcf', skiprows=134, dtype={'#CHRO<' : str}) 
df = pd.read_table('/BiO/home/kobic/only_rs_22-5000.vcf', skiprows=18)

명령행 인터페이스 #

FASTA manipulator 만들기 - 명령행 옵션을 받아 FASTA 형식 변환하기

$ python fastaman.py -c reverse-complement -f 60 -i in.fasta -o out.fasta
$ python fastaman.py -c transcribe -f 60 < in.fasta > out.fasta

정답

#!python
import sys

def transcribe(sequence):
    return sequence.replace('T', 'U')

def reverse_complement(sequence):
    base_pairs = {
        'A': 'T',
        'T': 'A',
        'G': 'C',
        'C': 'G',
    }
    result = []
    for nucleotide in sequence:
        result.append(base_pairs[nucleotide])
    return ''.join(reversed(result))

def main(**kwargs):
    command = kwargs['command']
    infile = kwargs['infile']
    outfile = kwargs['outfile']
    columns = kwargs['columns']

    title = next(infile).strip()
    sequences = []
    for line in infile:
        sequences.append(line.strip())
    sequence = ''.join(sequences)

    if command == 'transcribe':
        sequence = transcribe(sequence)
    elif command == 'reverse-complement':
        sequence = reverse_complement(sequence)

    outfile.write('{} reverse complement\n'.format(title))
    while sequence:
        outfile.write('{}\n'.format(sequence[:columns]))
        sequence = sequence[columns:]

if __name__ == '__main__':
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument("-c", "--command", required=True,
            choices=('reverse-complement', 'transcribe'),
            help='Select the type of command')
    parser.add_argument('-f', '--columns', type=int, default=60,
            help='Length of columns')
    parser.add_argument('-i', '--infile', default=sys.stdin,
            type=argparse.FileType('r'),
            help='Intput fasta file name. default is standard in')
    parser.add_argument('-o', '--outfile', default=sys.stdout,
            type=argparse.FileType('w'),
            help='Output file name. default is standard out')

    args = parser.parse_args()
    main(**vars(args))

연습문제 1. VCF 파일을 읽고 PCA 그림을 그리는 프로그램 작성

$ cat group.txt
GroupA: NA001 NA003 NA005
GroupB: NA008 NA098 NA099

$ python vcf_pca.py --vcf=a.vcf --group=group.txt --out=a.png
(a.png 그림 생성)

유틸리티 실습 #

연습문제 2. N-glycosylation motif는 N{P}[ST]{P} 이다. input.txt 에 uniprot_id들이 다음처럼 있을 때, 각각의 FASTA정보를 인터넷에서 획득한 뒤, 서열에 N-glycosylation motif가 어느 위치에 있는지 출력하는 프로그램을 작성하시오. (UniProt URL은 http://www.uniprot.org/uniprot/uniprot_id.fasta)

단백질 모티프는 보통 다음처럼 표기한다. (Prosite)

  • []: 괄호내 있는것들 중 하나
  • {}: 괄호내 있는 것들 빼고 나머지중 하나
  • x: 아무거나 하나

실행 예는 다음과 같다.

#!python
$ cat input.txt
A2Z669 
B5ZC00
P07204_TRBM_HUMAN
P20840_SAG1_YEAST

$ python uniprot-n-glycosylation-sites.py < input.txt
B5ZC00
85 118 142 306 395
P07204_TRBM_HUMAN
47 115 116 382 409
P20840_SAG1_YEAST
79 109 135 248 306 348 364 402 485 501 614

정답 2

import sys
import urllib.request
import re

filename = []
for line in sys.stdin:
    line = re.sub(r'\n', '', line)
    filename.append(line)
for name in filename:
    data = urllib.request.urlopen('http://www.uniprot.org/uniprot/'+name+'.fasta')
    title  = next(data).strip()
    sequence = ''
    for line in data:
        sequence = sequence+str(line)
    fasta = re.sub(r"'|b|\\n", '', sequence)
    pattern = re.compile(r'N[^P][ST][^P]') #reg exp
    motif = []
    m = re.finditer(pattern, fasta)
    for match in m:
        motif.append(match.start()) # when it prints, add 1 b/c it counts from 0
    if motif:
        print(name)
        location = []
        for loc in motif:
            loc += 1
            location.append(loc)
        print(*location)
    else:
        pass

연습문제 3. 로또 (6/45) 당첨 번호가 모두 홀수일 확률은? (직접 세어서 계산해보기)

#!python
>>> import itertools
>>> range(1, 46)
>>> itertools.combinations

정답 3

#!python
>>> i = 0
>>> j = 0
>>> for lotto in itertools.combinations(range(1, 46), 6):
...     if all(n % 2 for n in lotto):
...         j += 1
...     i += 1
...
>>> j / i
0.012393647192285877
>>>

연습문제 4. Boys 4명과 girls 3명이 소개팅중이다. 적어도 3커플이 나올 수 있는 가지수는?

#!python
>>> import itertools
>>> boys = ["smith", 'neo', 'morphius', 'cyper']
>>> girls = ['trinity', 'oracle', 'suji']
>>> itertools.permutations

정답 4

#!python
>>> for selected_boys in itertools.permutations(boys, 3):
...     print(list(zip(girs, selected_boys)))
...
[('trinity', 'smith'), ('oracle', 'neo'), ('suji', 'morphius')]
[('trinity', 'smith'), ('oracle', 'neo'), ('suji', 'cyper')]
[('trinity', 'smith'), ('oracle', 'morphius'), ('suji', 'neo')]
[('trinity', 'smith'), ('oracle', 'morphius'), ('suji', 'cyper')]
[('trinity', 'smith'), ('oracle', 'cyper'), ('suji', 'neo')]
[('trinity', 'smith'), ('oracle', 'cyper'), ('suji', 'morphius')]
[('trinity', 'neo'), ('oracle', 'smith'), ('suji', 'morphius')]
[('trinity', 'neo'), ('oracle', 'smith'), ('suji', 'cyper')]
[('trinity', 'neo'), ('oracle', 'morphius'), ('suji', 'smith')]
[('trinity', 'neo'), ('oracle', 'morphius'), ('suji', 'cyper')]
[('trinity', 'neo'), ('oracle', 'cyper'), ('suji', 'smith')]
[('trinity', 'neo'), ('oracle', 'cyper'), ('suji', 'morphius')]
[('trinity', 'morphius'), ('oracle', 'smith'), ('suji', 'neo')]
[('trinity', 'morphius'), ('oracle', 'smith'), ('suji', 'cyper')]
[('trinity', 'morphius'), ('oracle', 'neo'), ('suji', 'smith')]
[('trinity', 'morphius'), ('oracle', 'neo'), ('suji', 'cyper')]
[('trinity', 'morphius'), ('oracle', 'cyper'), ('suji', 'smith')]
[('trinity', 'morphius'), ('oracle', 'cyper'), ('suji', 'neo')]
[('trinity', 'cyper'), ('oracle', 'smith'), ('suji', 'neo')]
[('trinity', 'cyper'), ('oracle', 'smith'), ('suji', 'morphius')]
[('trinity', 'cyper'), ('oracle', 'neo'), ('suji', 'smith')]
[('trinity', 'cyper'), ('oracle', 'neo'), ('suji', 'morphius')]
[('trinity', 'cyper'), ('oracle', 'morphius'), ('suji', 'smith')]
[('trinity', 'cyper'), ('oracle', 'morphius'), ('suji', 'neo')]

Suggested Pages #

0.0.1_20140628_0