如何让我的Python脚本更快?

时间:2014-12-04 22:00:55

标签: python performance bioinformatics fastq

我是Python的新手,我编写了一个(可能非常难看)脚本,它应该从fastq文件中随机选择一个序列子集。 fastq文件以每行四行的块存储信息。每个块中的第一行以字符“@”开头。我用作输入文件的fastq文件是36 GB,包含大约14,000,000行。

我试图重写一个使用过多内存的现有脚本,并设法减少了很多内存使用量。但脚本需要永远运行,我不明白为什么。

parser = argparse.ArgumentParser()
parser.add_argument("infile", type = str, help = "The name of the fastq input file.", default = sys.stdin)
parser.add_argument("outputfile", type = str, help = "Name of the output file.")
parser.add_argument("-n", help="Number of sequences to sample", default=1)
args = parser.parse_args()


def sample():
    linesamples = []
    infile = open(args.infile, 'r')
    outputfile = open(args.outputfile, 'w')
    # count the number of fastq "chunks" in the input file:
    seqs = subprocess.check_output(["grep", "-c", "@", str(args.infile)])
    # randomly select n fastq "chunks":
    seqsamples = random.sample(xrange(0,int(seqs)), int(args.n))
    # make a list of the lines that are to be fetched from the fastq file:
    for i in seqsamples:
        linesamples.append(int(4*i+0))
        linesamples.append(int(4*i+1))
        linesamples.append(int(4*i+2))
        linesamples.append(int(4*i+3))
    # fetch lines from input file and write them to output file.
    for i, line in enumerate(infile):
        if i in linesamples:
            outputfile.write(line)

grep-step几乎没有时间,但是超过500分钟后,脚本仍然没有开始写入输出文件。所以我想这是grep和最后一个for循环之间需要很长时间的步骤之一。但我完全不明白哪一步,以及我能做些什么来加快它。

4 个答案:

答案 0 :(得分:2)

根据linesamples的大小,if i in linesamples将需要很长时间,因为您通过infile的每次迭代搜索列表。您可以将其转换为set以改善查找时间。此外,enumerate效率不高 - 我用line_num构造替换了它,我们在每次迭代中都会增加。

def sample():
    linesamples = set()
    infile = open(args.infile, 'r')
    outputfile = open(args.outputfile, 'w')
    # count the number of fastq "chunks" in the input file:
    seqs = subprocess.check_output(["grep", "-c", "@", str(args.infile)])
    # randomly select n fastq "chunks":
    seqsamples = random.sample(xrange(0,int(seqs)), int(args.n))
    for i in seqsamples:
        linesamples.add(int(4*i+0))
        linesamples.add(int(4*i+1))
        linesamples.add(int(4*i+2))
        linesamples.add(int(4*i+3))
    # make a list of the lines that are to be fetched from the fastq file:
    # fetch lines from input file and write them to output file.
    line_num = 0
    for line in infile:
        if line_num in linesamples:
            outputfile.write(line)
        line_num += 1
    outputfile.close()

答案 1 :(得分:1)

你说grep完成运行很快,所以在这种情况下,而不是只使用grep计算@ have grep的出现次数输出它看到的每个@字符的字节偏移量(使用grep的-b选项)。然后,使用random.sample选择您想要的块。一旦您选择了所需的字节偏移量,请使用infile.seek转到每个字节偏移量并从那里打印出4行。

答案 2 :(得分:0)

尝试并行化代码。我的意思是这个。您有14,000,000行输入。

  1. 先处理你的grep并过滤你的行并将其写入filteredInput.txt
  2. 将filteredInput拆分为10.000-100.000行文件,例如filteredInput001.txt,filteredInput002.txt
  3. 在此拆分文件上处理我们的代码。将输出写入不同的文件,如output001.txt,output002.txt
  4. 将您的结果合并为最后一步。
  5. 因为您的代码根本不起作用。您也可以在这些过滤后的输入上运行代码。您的代码将检查filteredInput文件是否存在,并将了解他所处的步骤,并从该步骤继续。

    您也可以使用shell或python线程以这种方式使用多个python进程(在步骤1之后)。

答案 3 :(得分:0)

您可以使用Reservoir Sampling算法。使用此算法,您只需读取一次数据(无需提前计算文件行数),因此您可以通过脚本管道数据。有python的例子 维基百科页面中的代码。

在Heng Li的seqtk中还有一个用于fastq采样的C实现。