Python 替换嵌套for循环和值赋值以进行列表理解
我编写了一个函数,用于计算同一位置多个字符串中某些字符(Python 替换嵌套for循环和值赋值以进行列表理解,python,for-loop,optimization,list-comprehension,Python,For Loop,Optimization,List Comprehension,我编写了一个函数,用于计算同一位置多个字符串中某些字符(a、C、G和T)的出现次数,并将出现次数保存在字典中 例如,对于这两个字符串“ACGG”和“CAGT”,它应该返回: {'A': [1, 1, 0, 0], 'C': [1, 1, 0, 0], 'G': [0, 0, 2, 1], 'T': [0, 0, 0, 1]} 我想将下面的代码转换为列表理解,以优化它的速度。它使用两个嵌套for循环,输入基序是包含a、C、G和T的字符串列表 def CountWithPseudocounts(M
a
、C
、G
和T
)的出现次数,并将出现次数保存在字典中
例如,对于这两个字符串“ACGG”和“CAGT”,它应该返回:
{'A': [1, 1, 0, 0], 'C': [1, 1, 0, 0], 'G': [0, 0, 2, 1], 'T': [0, 0, 0, 1]}
我想将下面的代码转换为列表理解,以优化它的速度。它使用两个嵌套for循环,输入基序是包含a、C、G和T的字符串列表
def CountWithPseudocounts(Motifs):
count = {}
k = len(Motifs[0])
t = len(Motifs)
for s in 'ACGT':
count[s] = [0] * k
for i in range(t):
for j in range(k):
symbol = Motifs[i][j]
count[symbol][j] += 1
return count
我已尝试替换此列表函数底部的嵌套for循环:
count = [ [ count[Motifs[i][j]][j] += 1 ] for i in range(0, t) ] for j in range(0, k)]
In [25]: {ch:[(ch==i) + (ch==j) for i, j in zip(a, b)] for ch in chars}
Out[25]: {'T': [0, 0, 0, 1], 'G': [0, 0, 2, 1], 'C': [1, 1, 0, 0], 'A': [1, 1, 0, 0]}
它不起作用,可能是因为不允许我在列表中赋值+=1。如何解决这个问题?您可以使用zip()
:
如果你想要一本字典,你可以使用听写理解:
count = [ [ count[Motifs[i][j]][j] += 1 ] for i in range(0, t) ] for j in range(0, k)]
In [25]: {ch:[(ch==i) + (ch==j) for i, j in zip(a, b)] for ch in chars}
Out[25]: {'T': [0, 0, 0, 1], 'G': [0, 0, 2, 1], 'C': [1, 1, 0, 0], 'A': [1, 1, 0, 0]}
或者,如果希望结果与角色列表的顺序相同,可以使用集合。OrderedDict
:
In [26]: from collections import OrderedDict
In [27]: OrderedDict((ch, [(ch==i) + (ch==j) for i, j in zip(a, b)]) for ch in chars)
Out[28]: OrderedDict([('A', [1, 1, 0, 0]), ('C', [1, 1, 0, 0]), ('G', [0, 0, 2, 1]), ('T', [0, 0, 0, 1])])
如果您仍然需要更高的性能和/或您正在处理长字符串和更大的数据集,您可以使用Numpy通过矢量化方法来解决这个问题
In [61]: pairs = np.array((list(a), list(b))).T
In [62]: chars
Out[62]:
array(['A', 'C', 'G', 'T'],
dtype='<U1')
In [63]: (chars[:,None,None] == pairs).sum(2)
Out[63]:
array([[1, 1, 0, 0],
[1, 1, 0, 0],
[0, 0, 2, 1],
[0, 0, 0, 1]])
[61]中的:pairs=np.array((列表(a),列表(b)).T
在[62]中:字符
出[62]:
数组(['A','C','G','T'],
dtype='您确实不能在列表理解中执行赋值(您可以通过调用函数来执行副作用)。列表理解需要一个表达式。此外,您希望将赋值给count
并同时更新旧的count
,这很奇怪
对于字典理解和列表理解,一种效率不高的方法是:
chars = 'ACGT'
a = 'ACGG'
b = 'CAGT'
sequences = list(zip(a,b))
counts = {char:[seq.count(char) for seq in sequences] for char in chars}
(对于序列计数(字符)
建议,贷记到)
这将产生:
{'G': [0, 0, 2, 1], 'A': [1, 1, 0, 0], 'C': [1, 1, 0, 0], 'T': [0, 0, 0, 1]}
通过使用更多字符串调用zip(…)
,您可以很容易地概括出计算更多字符串的解决方案
您还可以决定优化算法本身。这可能会更有效,因为您只需在字符串上循环一次,就可以使用字典查找,如:
def CountWithPseudocounts(sequences):
k = len(sequences[0])
count = {char:[0]*k for char in 'ACGT'}
for sequence in sequences:
j = 0
for symbol in sequence:
count[symbol][j] += 1
j += 1
return count
编辑:
如果要将一个元素添加到计数中的所有元素,可以使用:
counts = {char:[seq.count(char)+1 for seq in sequences] for char in chars}
counts={char:[seq.count(char)+1表示序列中的seq]表示char中的char}
如果速度性能真的很重要,我建议使用@Kasramvs提供的numpy方法
此外,计数字符甚至对现代计算机都不友好,也许你可以在计数前玩一些关于输入字符串索引/散列的技巧。例如,由于每个字符串只有4个字符,每个字符只包含4个可能的字母,“A”、“C”、“G”、“T”,因此它可以很容易地表示所有“ACG”从“AAAA”到“TTTT”的T组合,带有数字、哈希或神秘代码。组合的数量应等于或小于4x4=256个不同的数字
然后计算代码。例如,每次在python列表或numpy数组中看到“AAAA”并将其计为0x0时,看到“AAAC”并将其计为0x1,反之亦然。之后,将得到一个索引范围为0x0~0xFF(255)的binning数组和相应的出现次数,对吗?现在记住,在你的例子中,一个0x0代表A:{1,1,1,1},或者七个0x1代表A:{7,7,7,0}和C:{0,0,0,7}……仔细地求和它们,就是结果
在这种情况下,这些技巧可能有助于大大提高速度性能。速度的好处有两个:第一个是,现在计算机处理的是数字而不是字符,而数字比字符更容易排序、计数、分组、分区或索引;第二个是缓存命中率更高在这些技巧中,由于记忆痕迹大大减少,因此本质上是te
我希望这能有所帮助
那么,添加一些代码如下,作为一个明确的参考
首先,进口:
import itertools
import numpy as np
如果dictt02
不是太大,例如通常小于1M个键值对,则下面的函数encode_sequence()
应该足够快:
def encode_sequence(tsq):
t00 = itertools.product('ACGT', repeat=4)
t01 = np.array(list(t00)).view('|U4').ravel()
t02 = dict(zip(t01, np.arange(len(t01))))
t03 = [t02[i] for i in tsq]
return t03
并使用下面的代码片段生成一个张量,map\u decode
,表示有关“计算代码”的内容。。。此外,在这一部分下面还有一个称为增广矩阵变换的数学技巧,即它以相同的方式将ll0
和'ACGT'
变换为spanmap\u decode
,以供以后使用
ll0 = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])
map_decode = np.array(list(itertools.product(ll0, repeat=4)))
输入测试序列并进行翻译
test_seq = ('ACGG', 'CAGT', 'CCGA', 'AAAT', 'TTGC', 'GCAT', 'ACGG', 'AAAT')
u01 = encode_sequence(test_seq)
统计发生次数;请注意,下面的块应该是速度提高的主要来源,因为计算机擅长处理u01
中的数字
p01, q01 = np.unique(u01, return_counts=True)
毕竟,生成输出。。。这里有点棘手,例如p01
是test\u-seq
的排序哈希码,q01
确实是相应的计数,而map\u-decode
就像我说的,是一个张量,将p01
中的哈希码映射到我们想要的另一个向量,例如,将0x0(或“AAAA”)映射到a:[1,1,1,1],C:[0,0,0],G:[0,0,0,0]和T:[0,0,0,0]。因此,映射的map\u decode[p01]
通过计数q01
进行加权,并准备为报告求和:
np.sum(map_decode[p01]*q01[:, None, None], axis=0).T
它说
array([[4, 3, 3, 1],
[2, 4, 0, 1],
[1, 0, 5, 2],
[1, 1, 0, 4]])
它等价于A:{4,3,3,1},C:{2,4,0,1},G:{1,0,5,2}和T:{1,1,0,4}。检查是否符合答案
这就是numpy的实现;它在主体中不包含显式循环。除此之外,encode_sequence()
还可以被一些离线输入准备所取代,以提前提高性能。虽然我没有上述代码片段的速度度量,但我认为它们应该被加速到一定程度
好的,让我们讨论一下如果有长字符串会发生什么
我们以这个序列为例
test_seq0 = ((
'A'*40, 'A'*40, 'A'*40, 'C'*40, 'C'*40,
'C'*40, 'C'*40, 'G'*40, 'G'*40, 'T'*40
))*4
测试包含40个strin
def encode_sequence_longstring(tsq_np):
t00 = itertools.product('ACGT', repeat=4)
t01 = np.array(list(t00)).view('|U4').ravel()
t02 = dict(zip(t01, np.arange(len(t01))))
t03 = np.empty_like(tsq_np, dtype=np.uint)
t03.ravel()[:] = [t02[i] for i in tsq_np.ravel()]
return t03
In: v01 = np.asarray(test_seq0).view('|U4').reshape(-1, int(40/4))
In: v01
Out:
array([['AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA'],
['AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA'],
['AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA', 'AAAA'],
['CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC'],
['CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC'],
... skip 30 lines ...
['CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC'],
['CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC', 'CCCC'],
['GGGG', 'GGGG', 'GGGG', 'GGGG', 'GGGG', 'GGGG', 'GGGG', 'GGGG', 'GGGG', 'GGGG'],
['GGGG', 'GGGG', 'GGGG', 'GGGG', 'GGGG', 'GGGG', 'GGGG', 'GGGG', 'GGGG', 'GGGG'],
['TTTT', 'TTTT', 'TTTT', 'TTTT', 'TTTT', 'TTTT', 'TTTT', 'TTTT', 'TTTT', 'TTTT']],
dtype='<U4')
In: u02 = encode_sequence_longstring(v01)
In: u02
Out:
array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85],
[ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85],
... skip 30 lines ...
[ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85],
[ 85, 85, 85, 85, 85, 85, 85, 85, 85, 85],
[170, 170, 170, 170, 170, 170, 170, 170, 170, 170],
[170, 170, 170, 170, 170, 170, 170, 170, 170, 170],
[255, 255, 255, 255, 255, 255, 255, 255, 255, 255]],
dtype=uint64)
s01 = np.empty((4, 0))
for u03 in u02.T:
p02, q02 = np.unique(u03, return_counts=True)
s02 = np.sum(map_decode[p02]*q02[:, None, None], axis=0).T
s01 = np.hstack((s01, s02))
In: s01
Out:
array([[ 12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12.,
12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12.,
12., 12., 12., 12., 12., 12., 12., 12., 12., 12., 12.,
12., 12., 12., 12., 12., 12., 12.],
[ 16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16.,
16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16.,
16., 16., 16., 16., 16., 16., 16., 16., 16., 16., 16.,
16., 16., 16., 16., 16., 16., 16.],
[ 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8.,
8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8.,
8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8.,
8., 8., 8., 8., 8., 8., 8.],
[ 4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4.,
4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4.,
4., 4., 4., 4., 4., 4., 4., 4., 4., 4., 4.,
4., 4., 4., 4., 4., 4., 4.]])
test_seq0 = ((
'A'*40, 'A'*40, 'A'*40, 'C'*40, 'C'*40,
'C'*40, 'C'*40, 'G'*40, 'G'*40, 'T'*40
))*4000
array([[ 12000., 12000., 12000., 12000., 12000., 12000., 12000.,
12000., 12000., 12000., 12000., 12000., 12000., 12000.,
12000., 12000., 12000., 12000., 12000., 12000., 12000.,
12000., 12000., 12000., 12000., 12000., 12000., 12000.,
12000., 12000., 12000., 12000., 12000., 12000., 12000.,
12000., 12000., 12000., 12000., 12000.],
[ 16000., 16000., 16000., 16000., 16000., 16000., 16000.,
16000., 16000., 16000., 16000., 16000., 16000., 16000.,
16000., 16000., 16000., 16000., 16000., 16000., 16000.,
16000., 16000., 16000., 16000., 16000., 16000., 16000.,
16000., 16000., 16000., 16000., 16000., 16000., 16000.,
16000., 16000., 16000., 16000., 16000.],
[ 8000., 8000., 8000., 8000., 8000., 8000., 8000.,
8000., 8000., 8000., 8000., 8000., 8000., 8000.,
8000., 8000., 8000., 8000., 8000., 8000., 8000.,
8000., 8000., 8000., 8000., 8000., 8000., 8000.,
8000., 8000., 8000., 8000., 8000., 8000., 8000.,
8000., 8000., 8000., 8000., 8000.],
[ 4000., 4000., 4000., 4000., 4000., 4000., 4000.,
4000., 4000., 4000., 4000., 4000., 4000., 4000.,
4000., 4000., 4000., 4000., 4000., 4000., 4000.,
4000., 4000., 4000., 4000., 4000., 4000., 4000.,
4000., 4000., 4000., 4000., 4000., 4000., 4000.,
4000., 4000., 4000., 4000., 4000.]])
s1 = 'ACGG'
s2 = 'CAGT'
from collections import Counter
counter = Counter(enumerate(s1))
counter += Counter(enumerate(s2))
sorted(counter.items())
[((0, 'A'), 1),
((0, 'C'), 1),
((1, 'A'), 1),
((1, 'C'), 1),
((2, 'G'), 2),
((3, 'G'), 1),
((3, 'T'), 1)]
[counter[i,'A'] if (i,'A') in counter else 0 for i in range(4)]
[1, 1, 0, 0]
[counter[i,'C'] if (i,'C') in counter else 0 for i in range(4)]
[1, 1, 0, 0]
[counter[i,'G'] if (i,'G') in counter else 0 for i in range(4)]
[0, 0, 2, 1]
[counter[i,'T'] if (i,'T') in counter else 0 for i in range(4)]
[0, 0, 0, 1]