双循环需要时间

时间:2016-09-21 00:43:56

标签: python for-loop

我有一个脚本需要花费很多时间,并且在2天后无法完成... 我将1个文件解析为2个字典,如下所示:

gfftree = {'chr1':[(gene_id, gstart, gend),...], 'chr2':[(gene_id, gstart, gend),...],...}
TElocation = {'chr1':[(TE_id, TEstart, TEend),...], 'chr2':[(TE_id, TEstart, TEend),...],...}

- 目的是找到其TEstart或TEend或两者位于gene_id'之间的TE_id。每个chr(键)中的gstart和gend。

以上内容应改为"找到TE_id,其范围(TEstart,TEend)与任何gene_id的范围重叠(gstart,gend)"

这是我的代码:

TE_in_TSS = []
for TErange in TElocation[chromosome]:
    TE_id, TEstart, TEend = TErange
    for item in gfftree[chromosome]:
        gene, gstart, gend = item       
        if len(list(set(range(int(gstart),int(gend)+1)) & set(range(int(TEstart),int(TEend)+1)))) > 0:
            TE_in_TSS.append((gene, TE_id, TEstart, TEend))
        else:
            pass

到目前为止,我确定这个循环对于小数据很好,但是当涉及到800,000 TE_id和4,000 gene_id等更大的循环时,需要时间......而且我不知道它是否可以光洁度...

3 个答案:

答案 0 :(得分:3)

OP方法是O(n*m),其中n是基因数,m是TE数。这种方法不是像OP中那样针对每个TE测试每个基因,而是利用基因和TE的有序性质以及指定的匹配规则,仅查看每个基因和TE一次,除了描述的基因预测在3.下面。这种方法是O(n + m),前提是平均基因前瞻相对于n较小。访问每个基因和TE的顺序描述如下:

  1. 在我们完成对当前基因的当前TE测试之后,我们 得到下一个TE。
  2. 当前TE的起始位置超过当前基因的结束时间 位置,我们得到下一个基因,直到它没有。
  3. 如果我们找到匹配的TE /基因对,我们测试每个连续的基因 对抗当前的TE,直到没有匹配,留下当前的 基因不变。
  4. def get_TE_in_TSS(genes, TEs):
        TE_in_TSS = []
        gene_pos, TE_pos = 0, 0
        gene_count, TE_count = len(genes), len(TEs)
        while gene_pos < gene_count:
            while (TE_pos < TE_count) and (TEs[TE_pos][1] <= genes[gene_pos][2]):
                match_gene_pos = gene_pos
                while (match_gene_pos < gene_count) and (TEs[TE_pos][2] >= genes[match_gene_pos][1]):
                    TE_in_TSS.append((genes[match_gene_pos][0], TEs[TE_pos][0],
                                      TEs[TE_pos][1], TEs[TE_pos][2]))
                    match_gene_pos += 1 # look ahead to see if this TE matches the next gene
                TE_pos += 1
            gene_pos += 1
        return TE_in_TSS
    
    OP报告

    性能

    1 second (compared to 2 days + for OP code) for 801,948 TEs, 6,007 genes
    

    测试数据:

    genes = (('HTR3A', 7, 9), ('ADAMTSL4', 10,100), ('THSD4',2000, 2800), ('PAPLN', 2850, 3000))
    TEs = (('a', 10, 11), ('b', 13, 17), ('c', 50, 2500), ('d', 2550, 2700),
           ('e', 2800, 2900), ('f', 9999, 9999)) 
    TE_in_TSS = get_TE_in_TSS(genes, TEs)
    print(TE_in_TSS)
    

    输出:

    [('ADAMTSL4', 'a', 10, 11), ('ADAMTSL4', 'b', 13, 17), ('ADAMTSL4', 'c', 50, 2500), 
     ('THSD4', 'c', 50, 2500), ('THSD4', 'd', 2550, 2700), ('THSD4', 'e', 2800, 2900), 
     ('PAPLN', 'e', 2800, 2900)]
    

    请注意,此帖子的前9条评论指的是更为有效的O(n * m)方法,该方法因澄清规范而过时。

答案 1 :(得分:1)

这是一个使用多线程的解决方案,比较用于嵌套循环方法的代码。

我创建了两个csv,一个有8k行,一个800行(int,float1,float2)随机生成的数字,导入如下:

import time
import itertools 

start = time.time()

def f((TE_id, TEstart, TEend)):
    a=[]
    for gene, gstart, gend in gfftree['chr1']:
        if (gstart <= TEstart <=gend) or (gstart<=TEend <=gend):
            a.append((gene,TE_id,TEstart,TEend))
    return a

'''
#slow
TEinTSS = []
for TE_id, TEstart, TEend in TElocation['chr1']:
    for gene, gstart, gend in gfftree['chr1']:
        if (gstart <= TEstart <=gend) or (gstart<=TEend <=gend):
            TEinTSS.append((gene,TE_id,TEstart,TEend))
print len(TEinTSS)
print time.time()-start

#faster
TEinTSS = []
for things in TElocation['chr1']:
    TEinTSS.extend(f(things))
print len(TEinTSS)
print time.time()-start
'''

#fastest (especially with multi-core, multithreading)
from multiprocessing import Pool

if __name__ == '__main__':
    p=Pool()
    TEinTSS = list(itertools.chain.from_iterable(p.imap_unordered(f, b)))   
    print len(TEinTSS)
    print time.time() - start

答案 2 :(得分:1)

如果该过程的目的纯粹是为了找到落在特定起始范围内的基因ID而且您并不太担心如何实现这一目标,而只是寻找最快的解决方案,那么你可能想要考虑完全放弃循环的概念并查看预先存在的解决方案机制。

假设您的数据是CSV格式,以下内容符合您的要求,返回包含ID,基因名称和相关chromasones的数据框,按chromasone分组。

文件:genometest.py

import pandas as pd
columns = ['id', 'chromasone', 'start', 'end', 'gene_name']

te_locations = pd.read_csv('Sequences/te.bed', delimiter='\t', header=None, names=columns)
gene_locations = pd.read_csv('Sequences/gene.bed', delimiter='\t', header=None, names=columns)

dataframe = pd.merge(te_locations, gene_locations, on=['gene_name', 'chromasone'], how='outer', suffixes=('_te', '_ge'))
dataset = dataframe.query('start_te >= start_ge & start_te <= end_ge')[['peak_id_te', 'gene_name', 'chromasone']]
dataset.groupby('chromasone')

输入尺寸

  • TE_Locations数据集大小= 337848
  • Gene_Locations数据集大小= 50307

输出尺寸

  • 数据集大小= 7085

<强>性能

$ python3 -m timeit 'import genometest'
10 loops, best of 3: 0.391 usec per loop