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
如果dict
t02
不是太大,例如通常小于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'
变换为span
map\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]