使用一个衬里而不是 for 循环对 Python 中的字典中的字典中的选定元素求和

Sum selected elements in dict of dicts in Python using one liner instead of for-loop

我使用了下面的字典理解

dimer = {(ab+cd):{"1":0,"2":0,"3":0} for cd in 'ACGT' for ab in 'ACGT'}

生成一个dict of dicts,dimer:

dimer = {"AA":{"1":0,"2":0,"3":0}, "AC":{"1":0,"2":0,"3":0}, "AG":{"1":0,"2":0,"3":0}, "AT":{"1":0,"2":0,"3":0}, "CA":{"1":0,"2":0,"3":0}, "CC":{"1":0,"2":0,"3":0}, "CG":{"1":0,"2":0,"3":0}, "CT":{"1":0,"2":0,"3":0}, "GA":{"1":0,"2":0,"3":0}, "GC":{"1":0,"2":0,"3":0}, "GG":{"1":0,"2":0,"3":0}, "GT":{"1":0,"2":0,"3":0}, "TA":{"1":0,"2":0,"3":0}, "TC":{"1":0,"2":0,"3":0}, "TT":{"1":0,"2":0,"3":0}, "TG":{"1":0,"2":0,"3":0}}

不过,现在我想对所选元素进行总结,

如果我将它们硬编码出来,就像

total_A = dimer["AA"]["1"]+dimer["CA"]["1"]+dimer["GA"]["1"]+dimer["TA"]["1"]+dimer["AA"]["2"]+dimer["CA"]["2"]+dimer["GA"]["2"]+dimer["TA"]["2"]+dimer["AA"]["3"]+dimer["CA"]["3"]+dimer["GA"]["3"]+dimer["TA"]["3"]
total_C = dimer["AC"]["1"]+dimer["CC"]["1"]+dimer["GC"]["1"]+dimer["TC"]["1"]+dimer["AC"]["2"]+dimer["CC"]["2"]+dimer["GC"]["2"]+dimer["TC"]["2"]+dimer["AC"]["3"]+dimer["CC"]["3"]+dimer["GC"]["3"]+dimer["TC"]["3"]
total_G = dimer["AG"]["1"]+dimer["CG"]["1"]+dimer["GG"]["1"]+dimer["TG"]["1"]+dimer["AG"]["2"]+dimer["CG"]["2"]+dimer["GG"]["2"]+dimer["TG"]["2"]+dimer["AG"]["3"]+dimer["CG"]["3"]+dimer["GG"]["3"]+dimer["TG"]["3"]
total_T = dimer["AT"]["1"]+dimer["CT"]["1"]+dimer["GT"]["1"]+dimer["TT"]["1"]+dimer["AT"]["2"]+dimer["CT"]["2"]+dimer["GT"]["2"]+dimer["TT"]["2"]+dimer["AT"]["3"]+dimer["CT"]["3"]+dimer["GT"]["3"]+dimer["TT"]["3"]

我想出的最好的简化方法是使用嵌套的 for 循环:

total_0 = {i:0 for i in 'ACGT'}   
for i in 'ACGT':    
    for j in 'ACGT':
        for k in '123':
            total_0[i] += dimer[j+i][k]  

我想知道有没有什么方法可以用一根线来总结它们?

我还有另一个嵌套的 for 循环:

row_sum = {i:{"1":0,"2":0,"3":0} for i in 'ACGT'}   
for i in 'ACGT':    
    for j in 'ACGT':
        for k in '123': 
            row_sum[i][k] += float(dimer[i+j][k])

硬编码版本如下:

row_sum = {"A":{"1":0,"2":0,"3":0},"C":{"1":0,"2":0,"3":0},"G":{"G":0,"2":0,"3":0},"T":{"1":0,"2":0,"3":0}} 
for i in range(1,4,1): 
    row_sum["A"][str(i)] = float(dimer["AA"][str(i)]+dimer["AC"][str(i)]+dimer["AG"][str(i)]+dimer["AT"][str(i)])
    row_sum["C"][str(i)] = float(dimer["CA"][str(i)]+dimer["CC"][str(i)]+dimer["CG"][str(i)]+dimer["CT"][str(i)])
    row_sum["G"][str(i)] = float(dimer["GA"][str(i)]+dimer["GC"][str(i)]+dimer["GG"][str(i)]+dimer["GT"][str(i)])
    row_sum["T"][str(i)] = float(dimer["TA"][str(i)]+dimer["TC"][str(i)]+dimer["TG"][str(i)]+dimer["TT"][str(i)])

我也想知道是否有任何方法可以使用一个衬里来总结第二个嵌套的for循环?

抱歉,我是 Python 的新手。任何帮助将不胜感激!

首先,您可以使用像这样的笛卡尔积将 3 个循环合并为一个循环。

from itertool import product
row_sum = {i: {"1": 0, "2": 0, "3": 0} for i in NT}   
for i, j, k in product('ACGT', 'ACGT', '123'):    
    row_sum[i][k] += float(dimer[i + j][k])

这是单行本,但如果您是 Python

的新手,可能很难理解
{i: sum(sum(dimer[i + j].values()) for j in 'ACGT') for i in 'ACGT'}

我不知道这将如何与您的程序的其余部分相结合,但可能值得切换到不同的数据结构。如果您将二聚体集合表示为单个 numpy 整数数组,您将拥有单行代码,并且还会看到大幅加速。例如,您的二聚体可以这样表示:

import numpy as np
dimer = np.zeros((4,4,3),dtype=int)

这里第一个索引中的0,1,2,3表示二聚体中的第一个元素是否为A,C,G,T,第二个索引同理,而第三个索引包含三种不同的情况您标记为“1”、“2”或“3”。所以你的 dimer["AG"]["1"] 在这里会是 dimer[0,2,0] (因为 numpy 从零开始计数)。

使用这样的结构的好处是

  1. 如果您的数据集变大(例如,如果每个二聚体有 300000 个元素而不是 3 个元素),它会更快并且内存效率更高。
  2. 有很多函数可用于操作 numpy 数组。例如,np.sum(dimer,2) 会给出每个二聚体的元素总数。

您想要的汇总统计数据可以计算为:

total_0 = np.sum(dimer, (0,2))
row_sum = np.sum(dimer, 1)

作为速度差异的说明,对于您的问题大小,使用 for 循环的 dict 方法需要 20 微秒来计算 total_0,而 numpy 总和需要 5.7微秒。对于一个大 1000 倍的问题,其中每个二聚体有 3000 个成员,dict 方法需要 22 毫秒,而 numpy 需要 31 微秒。对于一个大 1,000,000 倍的问题,dict 需要 24.5 秒,而 numpy 需要 24.3 毫秒。所以对于大问题,numpy 比使用字典快 1000 倍。