Python:使用位。用零和1编码核苷酸

时间:2015-01-09 21:51:02

标签: python bit bioinformatics

我想在Python中使用位编码对核苷酸'A','G','C'和'T'进行编码。例如:

'A' = 00
'G' = 01
'C' = 10
'T' = 11

为了建立一个包含k-mers的巨大词典,如:

dic = { 'ATGACTGACT':231, 'AAATGACGGAC':500 ... }

我认为这可以减少该dict所需的内存量,因为'ATGC'需要4个字节,但同一个字需要8位并进行位编码。

我不确定是否可以这样做,如果是这样,我怎么能用Python

来做

提前致谢!

编辑:对不起,我没有正确解释自己。

我想要的是通过一个由大小为k的滑动窗口的'ATGC'组成的序列,并计算每个k-mer在该seq中出现的次数。例如:

'ATGAATGAA' # with a sliding window of 5 would be
 dic = { 'ATGAA':2, 'TGAAT':1, 'GAATG':1, 'AATGA':1, }

由于我想在开始读取序列之前构造具有大小为k的'AGTC'的所有可能组合的dict,以便以每个k-mer作为键来访问该dict并且将其值加1,我想知道是否有可能将该字典中的k-mers存储为位编码。或多或少:

dic = {1011001010: 3, 0000110011: 666, ...  etc }

目前我正在用itertools构建那个dict。

# k-mers of size 8
{''.join(x):0 for x in itertools.product('ATGC', repeat=8)}

我想另一个问题是每个k-mer都需要转换为该位编码才能访问dict

3 个答案:

答案 0 :(得分:4)

您可以将您的kmers转换为二进制文件,但正如Ignacio指出您仍然需要知道它们的长度,因此您可能还需要存储它。因此,对于很长的序列,这仍然可以节省内存空间。

这是一些示例代码,它接受序列,对它们进行编码并再次对它们进行解码:

encoding_map = {'A': 0, 'G': 1, 'C': 2, 'T': 3}
decoding_lst = ['A', 'G', 'C', 'T']


def encode(k):
    code = 0
    for ch in k:
        code *= 4
        code += encoding_map[ch]
    return code, len(k)


def decode(enc):
    code, length = enc
    ret = ''
    for _ in range(length):
        index = code & 3
        code >>= 2
        ret = decoding_lst[index] + ret
    return ret


kmers = ['ATGACTGACT', 'ATGC', 'AATGC']

kmerdict = {k: encode(k) for k in kmers}

print(kmerdict)

for key, enc in kmerdict.items():
    print(enc, decode(enc))

典型输出:

{'AATGC': (54, 5), 'ATGC': (54, 4), 'ATGACTGACT': (215883, 10)}
(54, 5) AATGC
(54, 4) ATGC
(215883, 10) ATGACTGACT

顺便说一句,序列有多长并不重要,Python应该能够处理编码和解码,因为整数扩展到足够的位来保存数字。

答案 1 :(得分:1)

这完全符合您的要求

In [11]: d={'A':'00','G':'01','C':'10','T':'11'}

In [12]: int('0B'+''.join([d[c] for c in 'ATGACTGACT']),2)
Out[12]: 215883

In [13]: int('0B'+''.join([d[c] for c in 'ATGACTGACT'[::-1]]),2)
Out[13]: 925212

In [14]: 

pmod Ignacio Vazquez-Abrams 的评论中提出的异议非常重要,我认为你应该认真重新考虑你的方法。

答案 2 :(得分:0)

正如@ gbofi的回答所示,将k-mer转换为04**k - 1之间的整数非常简单。另一种主要是数学编码的方法是:

def kmer_to_int(kmer):
    return sum(4**i * "ATGC".index(x) for i, x in enumerate(kmer))

我没有测试过这是否比构建二进制字符串然后将其转换为int更快。

此代码为输入中的第一个字符提供最低位位置,因此"AT"变为0b0100,或4"TA"变为0b0001或{ {1}}。如果您希望编码将首要字母视为最重要的字母,请在生成器表达式中使用1而不是enumerate(reversed(kmer))

正如其他人所评论的那样,这些整数仅对给定长度enumerate(kmer)是唯一的。如果相同的整数仅作为尾随k的数量(例如A"ATG""ATGA",{"ATGAA""ATGAAA", {1}}等等都编码为36)。

至于你在更大的序列中计算特定k-mers出现次数的更广泛的目标,我不确定你是否会看到以这种方式编码k-mers的优势。好处可能取决于数据集的详细信息。

整数键的一个优点是它们允许您使用列表而不是字典来保存您的计数。您可以使用lst = [0] * 4**k构建合适的列表,然后增加您使用lst[kmer_to_int(kmer)] += 1看到的值。在给定相同数量的条目的情况下,列表确实具有比字典更低的开销,但是我不确定差异是否足够大以使其有用。

如果您的数据稀疏分布(即,许多4 ** k个可能的k-mer序列永远不会出现在您的输入中),使用列表可能仍会浪费大量内存,因为列表总是{{ 1}}元素长。更好的方法可能是使用其他一些方法来简化稀疏数据的4**k代码。

一种选择是使用某些dict类方法,以避免将结果集中的所有值初始化为dict。如果您将增量代码更改为0,则无论d[key] = d.get(key, 0) + 1是否已在字典中,它都将起作用。

另一种选择是使用key而不是常规collections.Counterdict类专门用于计算输入序列中项目的实例,这似乎正是您正在做的事情。它认为任何已经看过的密钥都没有Counter