Python:使用>的输入加速脚本十亿行

时间:2017-05-07 11:19:47

标签: python performance python-2.7

我已经看到了很多关于如何加速python代码或提高效率的技巧。我在下面的代码中尝试了一些方法,例如:尽可能将全局变量更改为局部变量,使用.format创建字符串而不是添加字符串,尝试不创建多个变量。但是,这个脚本需要1h25才能运行。我有两个输入文件:

1)床文件,两列(制表符分隔)文件,第一列中包含数字或代码,第二列中包含数字。它有大约20亿行,其中数字的组合是唯一的(它具有基因组中的所有位置;第一列是染色体,第二列是位置):

  

1 1

     

1 2

     

1 3

     

1 4

     

...

2)一个复杂的文件,其中前几个(~3000行)是以#开头的标题,然后是前两列中数字/代码+数字组合的条目。这两列与第一个文件建立链接(文件1中的1 1与文件2中的1 1相同)。这有大约2200万行。以下是前三行的示例:

1   1   .   G   .   32.9939 .   DP=1;MQ0F=0;AF1=0;AC1=0;DP4=1,0,0,0;MQ=60;FQ=-29.9923   GT:PL:GQ    0/1:0:36
1   2   .   T   .   32.9939 .   DP=1;MQ0F=0;AF1=0;AC1=0;DP4=1,0,0,0;MQ=60;FQ=-29.9923   GT:PL:GQ    ./.:0:36
1   3   .   C   .   32.9939 .   DP=1;MQ0F=0;AF1=0;AC1=0;DP4=1,0,0,0;MQ=60;FQ=-29.9923   GT:PL:GQ    1/1:0:36

问题:我想过滤第一个文件中的行,如果第二个文件中的那些行在最后一列中有0 / 0,0 / 1或1/1(第4种可能性是./。)我需要解析最后一列,以达到这三个字符)

增加的复杂性是必须通过来自另一个程序的管道读取文件#2,因为它是以该程序的特定方式进行压缩(打开此文件需要很长时间,但我无能为力...)

致电:程序视图file2.vcf.gz | my_script.py file1.bed

import sys
import re
import time

start_time = time.time()


def make_vcf_dict(vcf):
    mydict={}
    for line in (line for line in vcf if not line.startswith("#")):
            line=line.strip().split()
            genotype=line[-1].split(':')[0]

            motif=re.compile('\./\.')
            if motif.match(genotype) is None:
                mydict.setdefault('{}:{}'.format(line[0],line[1]),'{}:{}'.format(line[0],line[1]))

    return mydict

def create_output_bed(bed,data):

    print "creating output"
    for line in (line for line in data if line.startswith('#CHROM')):
        output_name='{}_mask_positions.bed'.format(line.strip().split()[-1])
    print output_name
    output=open(output_name,'w') 

    print "making dictionary"   
    for line in bed:
        line=line.strip().split()
    #creating the same entry as in dict:
        region='{}:{}'.format(line[0], line[1])
        if region not in mydict:
            output.write('{}\t{}\n'.format(line[0],line[1]))
    output.close()
    bed.close()
    return

print "reading data"
data=sys.stdin.readlines()  #.readlines here is key!!

mydict=make_vcf_dict(data)

#read the bed file:
print "Writing output"
create_output_bed(open(sys.argv[1],'r'),data)

print("--- %s seconds ---" % (time.time() - start_time))

我想知道是否会有更有效的方法来处理这个问题?没有字典,拆分我的文件?我有一个32核服务器来处理这个和脚本的经验很少... 谢谢!

1 个答案:

答案 0 :(得分:1)

如果第二个文件只有几百万行(第一个不是十亿行),那么我希望数据能够适合内存。

  

我有一个32核服务器来处理这个

并行化并没有多大帮助,因为主要的瓶颈是磁盘,而不是CPU。除非数据分布在不同磁盘上的许多文件中。

但是,您可以做一些改进:

  • 将正则表达式编译移出循环(motif=re.compile('\./\.'))。
  • 使用set代替dict
  • 避免使用format,只需使用tuple
  • 请勿事先阅读所有内容。
  • 避免两次超过stdin。
  • 避免做任何两次。
import sys
import re
import time

start_time = time.time()

def make_vcf(vcf_input):
    output = set()
    motif=re.compile('\./\.')

    for line in vcf_input:
        line = line.strip().split()
        if line[0].startswith('#CHROM'):
            output_name = '{}_mask_positions.bed'.format(line[-1])
            continue
        elif line[0].startswith("#"):
            continue

        genotype=line[-1].split(':')[0]

        if motif.match(genotype) is None:
            output.add( (line[0],line[1]) )

    return output_name, output


def create_output_bed(output_name, vcf, bed):
    print "creating output:", output_name
    output = open(output_name,'w') 

    print "making dictionary"   
    for line in bed:
        line = line.strip().split()
        #creating the same entry as in dict:
        region = line[0], line[1]
        if region not in vcf:
            output.write('{}\t{}\n'.format(line[0],line[1]))
    output.close()
    bed.close()
    return

print "reading data"  
output_name, vcf = make_vcf(sys.stdin.readlines())

#read the bed file:
print "Writing output"
create_output_bed(output_name, vcf, open(sys.argv[1],'r'))

print("--- %s seconds ---" % (time.time() - start_time))