基于FM-index的序列比对算法

1. 什么是序列比对?

为确定两个或多个序列之间的相似性以至于同源性,而将它们按照一定的规律排列。

用个例子来看一下吧!

先看看数据是个啥。 读取Reads.fq文件,这是一个fastq格式的文件,存放了2条Read。read1, read2表示两条read的名字,名字下面是两条read对应的序列信息。


In [3]:
from Bio import SeqIO, pairwise2
handle = open("../data/Reads.fa", "r")
records = list(SeqIO.parse(handle, "fasta"))
handle.close()
for record in records:
    print record.id
    print record.seq


read1
ACATATATACCAGGACTCGTACAGTACGACCAGGAGAGACAGACTTAC
read2
ACCATATATACCAGGACACGTACAGTACCCAGGAGAGAGAGACTTAC

现在对这两个Read比对一下看看是什么效果


In [4]:
seq1 = str(records[0].seq)
seq2 = str(records[1].seq)
alignments = pairwise2.align.globalxx(seq1, seq2)
print alignments


[('AC-ATATATACCAGGACTCGTACAGTACGACCAGGAGAGACAGACTTAC', 'ACCATATATACCAGGACACGTACAGTAC--CCAGGAGAGAGAGACTTAC', 44.0, 0, 49), ('A-CATATATACCAGGACTCGTACAGTACGACCAGGAGAGACAGACTTAC', 'ACCATATATACCAGGACACGTACAGTAC--CCAGGAGAGAGAGACTTAC', 44.0, 0, 49)]

好像不太直观,换个方式来看一看


In [5]:
from Bio.pairwise2 import format_alignment
for aln in alignments : print(format_alignment(*aln))


AC-ATATATACCAGGACTCGTACAGTACGACCAGGAGAGACAGACTTAC
|||||||||||||||||||||||||||||||||||||||||||||||||
ACCATATATACCAGGACACGTACAGTAC--CCAGGAGAGAGAGACTTAC
  Score=44

A-CATATATACCAGGACTCGTACAGTACGACCAGGAGAGACAGACTTAC
|||||||||||||||||||||||||||||||||||||||||||||||||
ACCATATATACCAGGACACGTACAGTAC--CCAGGAGAGAGAGACTTAC
  Score=44

简单解释一下这个结果

这里出现了两个最佳比对,它们的Score都是44。 在每个最佳比对中,用竖线直观显示字符间对齐。 '-'表示空格, 例如,在第一个最佳比对中, read1的"AC-A"与read2的"ACCA"对齐,在第2个位置(位置从0开始)处read2比read1多了一个字符'C'。

我们管read2比read1在第2个位置处多了一个字符的情况,称为1个插入(insertion),反之,称为删除(deletion)。 对齐的字符如果相同,称为match,不同称为mismatch

总结一下

insertion:              '_' => 'A'
deletion:              'A' => '_'
match:                  'A' => 'A'
mismatch:               'A' => 'C'

把第一个最佳比对的mismatch、insertion和deletion都找出来


In [6]:
edit = [(i, j) for i, j in zip(alignments[0][0], alignments[0][1]) if i != j]
for e in edit: print e[0], '->', e[1]


- -> C
T -> A
G -> -
A -> -
C -> G

2. BWT, SA和FM-index

先说说什么是SA

给定一个字符串S, 它的序列是这个样子的。


In [42]:
S = 'ATCGAAGTG'

为了计算的方便,我们给s加一个'$',用来标示字符串结尾。


In [43]:
S += '$'

SA(suffix array)是一个数组,它是以字典序的方式保存了S中所有后缀在S中的开始位置。

下面继续看例子,我们用Pos数组存放了S的所有后缀的开始位置。


In [44]:
Pos = range(len(S))
print Pos


[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

以字典序的方式对Pos数组排序。


In [45]:
SA = sorted(Pos, key=lambda x:S[x:])
print SA


[9, 4, 5, 0, 2, 8, 3, 6, 1, 7]

再看一下SA这些位置对应的后缀。


In [46]:
for i in SA: print S[SA[i]:]


TG$
CGAAGTG$
G$
$
AGTG$
TCGAAGTG$
ATCGAAGTG$
GAAGTG$
AAGTG$
GTG$

放在一起对比一下。


In [47]:
print 'Index\tSA\tSuffix'
for i, p in enumerate(SA): print '{0}\t{1}\t{2}'.format(i, p, S[p:])


Index	SA	Suffix
0	9	$
1	4	AAGTG$
2	5	AGTG$
3	0	ATCGAAGTG$
4	2	CGAAGTG$
5	8	G$
6	3	GAAGTG$
7	6	GTG$
8	1	TCGAAGTG$
9	7	TG$

再来看一看什么是BWT

BWT是S的一个排列,其中BWT[i] = S[SA[i]-1]


In [48]:
BWT = ''.join([S[x-1]for x in SA])
print BWT


GGA$TTCAAG

再放一起看一下。


In [49]:
print 'Index\tSA\tBWT\tSuffix'
for i, p in enumerate(SA): print '{0}\t{1}\t{2}\t{3}'.format(i, p, S[p-1],S[p:])


Index	SA	BWT	Suffix
0	9	G	$
1	4	G	AAGTG$
2	5	A	AGTG$
3	0	$	ATCGAAGTG$
4	2	T	CGAAGTG$
5	8	T	G$
6	3	C	GAAGTG$
7	6	A	GTG$
8	1	A	TCGAAGTG$
9	7	G	TG$

发现了什么?

BWT[i]的字符是字符串s中SA[i]-1位置的字符,而Suffix[i]是S中SA[i]位置开始的后缀,也就是说其实BWT[i]是Suffix[i]的前一个字符。 为了更清楚的观察,我们这回用BWM矩阵,代表以字典序的方式排列S中的所有轮转(rotation)。


In [52]:
print 'Index\tSA\tBWT\tBWM'
for i, p in enumerate(SA): print '{0}\t{1}\t{2}\t{3}'.format(i, p, s[p-1],(s+s)[p:p+len(s)])


Index	SA	BWT	BWM
0	9	G	$ATCGAAGTG
1	4	G	AAGTG$ATCG
2	5	A	AGTG$ATCGA
3	0	$	ATCGAAGTG$
4	2	T	CGAAGTG$AT
5	8	T	G$ATCGAAGT
6	3	C	GAAGTG$ATC
7	6	A	GTG$ATCGAA
8	1	A	TCGAAGTG$A
9	7	G	TG$ATCGAAG

对比BWT和BWM发现,其实BWT就是BWM的最后一列。(想一想可以直接用SA来生成BMW)

来看一个比较有趣的问题

问题: 如果已知BWT串,我们是否能用BWT还原出原始串S? 答案是肯定的。

我们先用BWT还原出BWM的第一列, 只需将BWT排序就可获得BWM的第一列。(想想为什么?)


In [58]:
firstCol = ''.join(sorted(BWT))
print firstCol


$AAACGGGTT

BWT作为BWM的最后一列,和firstCol有啥关系呢?

我们换一个方式来表示BWM,我们用字符在S上的位置表示每个rotation。


In [63]:
print 'Index\tSA\tBWT\tBWM\'\tBWM'
for i, p in enumerate(SA): 
    print '{0}\t{1}\t{2}\t{3}\t{4}'.format(i, p, s[p-1],''.join(map(str, range(len(S))[p:]+range(len(s))[:p])),(s+s)[p:p+len(s)])


Index	SA	BWT	BWM'	BWM
0	9	G	9012345678	$ATCGAAGTG
1	4	G	4567890123	AAGTG$ATCG
2	5	A	5678901234	AGTG$ATCGA
3	0	$	0123456789	ATCGAAGTG$
4	2	T	2345678901	CGAAGTG$AT
5	8	T	8901234567	G$ATCGAAGT
6	3	C	3456789012	GAAGTG$ATC
7	6	A	6789012345	GTG$ATCGAA
8	1	A	1234567890	TCGAAGTG$A
9	7	G	7890123456	TG$ATCGAAG

对比BWM'与BWM发现以下的性质:

  1. 在BWM中最后一列(BWT)中的字符在S中的位置比BMW中第一列(firstCol)中的字符在S中的位置小1;(从BWM'中可以看出)
  2. 来自于S上同一位置的字符C,在BWM的第一列与最后一列中的所有字符C中的相对位置相同。例如BWM的第一列中第二个字符'A'与最后一列的第二个字符'A',都来自于S的位置5。

In [ ]: