2011-08-24 149 views
3

我需要处理超大的txt输入文件,而且我通常使用.readlines()来首先读取整个文件,并将其转换为列表。如何避免使用readlines()?

我知道这是真正的内存成本,可以说是相当缓慢的,但我也需要利用的LIST特性来操纵特定行,象下面这样:

#!/usr/bin/python 

import os,sys 
import glob 
import commands 
import gzip 

path= '/home/xxx/scratch/' 
fastqfiles1=glob.glob(path+'*_1.recal.fastq.gz') 

for fastqfile1 in fastqfiles1: 
    filename = os.path.basename(fastqfile1) 
    job_id = filename.split('_')[0] 
    fastqfile2 = os.path.join(path+job_id+'_2.recal.fastq.gz') 

    newfastq1 = os.path.join(path+job_id+'_1.fastq.gz') 
    newfastq2 = os.path.join(path+job_id+'_2.fastq.gz') 

    l1= gzip.open(fastqfile1,'r').readlines() 
    l2= gzip.open(fastqfile2,'r').readlines() 
    f1=[] 
    f2=[] 
    for i in range(0,len(l1)): 
     if i % 4 == 3: 
      b1=[ord(x) for x in l1[i]] 
      ave1=sum(b1)/float(len(l1[i])) 
      b2=[ord(x) for x in str(l2[i])] 
      ave2=sum(b2)/float(len(l2[i])) 
      if (ave1 >= 20 and ave2>= 20): 
       f1.append(l1[i-3]) 
       f1.append(l1[i-2]) 
       f1.append(l1[i-1]) 
       f1.append(l1[i]) 
       f2.append(l2[i-3]) 
       f2.append(l2[i-2]) 
       f2.append(l2[i-1]) 
       f2.append(l2[i]) 
    output1=gzip.open(newfastq1,'w') 
    output1.writelines(f1) 
    output1.close() 
    output2=gzip.open(newfastq2,'w') 
    output2.writelines(f2) 
    output2.close() 

在一般情况下,我想阅读整个文本的每一行,但如果第四行符合所需的条件,我会将这4行添加到文本中。 所以我可以避免readlines()来实现这一点? THX

编辑: 嗨,其实我自己找到了一个更好的办法:

import commands 
l1=commands.getoutput('zcat ' + fastqfile1).splitlines(True) 
l2=commands.getoutput('zcat ' + fastqfile2).splitlines(True) 

我想 '用zcat' 是超级快.... 前后花了15分钟改成readlines,而只有1分只用zcat ...

回答

6

如果你可以重构你的代码通过线性读取文件,那么你可以说for line in file遍历文件的每一行,而不会立即将其全部读入内存。但是,由于您的文件访问看起来更复杂,因此您可以使用生成器来替换readlines()。要做到这一点的方法之一是使用itertools.izipitertools.izip_longest

def four_at_a_time(iterable): 
    """Returns an iterator that returns a 4-tuple of objects at a time from the 
     given iterable""" 
    args = [iter(iterable) * 4] 
    return itertools.izip(*args) 
... 
l1 = four_at_a_time(gzip.open(fastqfile1, 'r')) 
l2 = four_at_a_time(gzip.open(fastqfile2, 'r')) 
for i, x in enumerate(itertools.izip(l1, l2)) 
    # x is now a 2-tuple of 4-tuples of lines (one 4-tuple of lines from the first file, 
    # and one 4-tuple of lines from the second file). Process accordingly. 
1

你可以使用enumerate遍历文件中的行,这会返回一个计数和行每次迭代:

with open(file_name) as f: 
    for i, line in enumerate(f): 
     if i % 4 == 3: 
      print i, line 
0

棘手,因为你实际上有两个文件,你正在同时处理。

您可以使用fileinput模块有效地一次解析一行文件。它也可用于解析文件列表,并且可以使用该块内的fileinput.nextfile()方法并行地交替处理多个文件,一次从每个文件中消耗一行。

fileinput.lineno()方法甚至会为您提供当前文件中的当前行号。你可以在循环体中使用临时列表来跟踪你的4行块。

完全未经测试特殊的代码,可能是基于对你的代码做什么误会,如下:

f1 = [] 
f2 = [] 
for line in fileinput(filename1, filename2): 
    if fileinput.filename() = filename1: 
     f1.append(line) 
    else: 
     f2.append(line) 
     if fileinput.lineno() % 4 == 3: 
      doMyProcesing() 
      f1 = []; f2 = [] 
    fileinput.nextfile() 
1

下面是如何打印包含foo和所有行前3行:

f = open(...) 
prevlines = [] 
for line in f: 
    prevlines.append(line) 
    del prevlines[:-4] 
    if 'foo' in line: 
    print prevlines 

如果你正在读2个文件在时间(与相等数量的行),做到这一点是这样的:

f1 = open(...) 
f2 = open(...) 
prevlines1 = [] 
for line1 in f1: 
    prevlines1.append(line1) 
    del prevlines1[:-4] 
    line2 = f2.readline() 
    prevlines2.append(line2) 
    del prevlines2[:-4] 
    if 'foo' in line1 and 'bar' in line2: 
    print prevlines1, prevlines2 
2

一个简单的方法是将,

(伪代码,可能含有错误,仅供参考)

a=gzip.open() 
    b=gzip.open() 

    last_four_a_lines=[] 
    last_four_b_lines=[] 

    idx=0 

    new_a=[] 
    new_b=[] 

    while True: 
     la=a.readline() 
     lb=b.readline() 
     if (not la) or (not lb): 
     break 

     if idx % 4==3: 
     a_calc=sum([ something ])/len(la) 
     b_calc=sum([ something ])/len(lb) 
     if a_calc and b_calc: 
      for line in last_four_a_lines: 
      new_a.append(line) 
      for line in last_four_b_lines: 
      new_b.append(line) 

     last_four_a_lines.append(la) 
     del(last_four_a_lines[0]) 
     last_four_b_lines.append(lb) 
     del(last_four_b_lines[0]) 
     idx+=1 
a.close() 
b.close() 
+0

这是一个简单的方法,亚当的建议是一个更好的方法 –

0

我觉得提升L1和L2的获得是不够的:你必须改进代码全球

我建议:

#!/usr/bin/python 

import os 
import sys 
import gzip 

path= '/home/xxx/scratch/' 

def gen(gfa,gfb): 
    try: 
     a = (gfa.readline(),gfa.readline(),gfa.readline(),gfa.readline()) 
     b = (gfb.readline(),gfb.readline(),gfb.readline(),gfb.readline()) 
     if sum(imap(ord,a[3]))/float(len(a[3])) >= 20 \ 
      and sum(imap(ord,b[3]))/float(len(b[3])) >= 20: 
      yield (a,b) 
    except: 
     break 

for fastqfile1 in glob.glob(path + '*_1.recal.fastq.gz') : 
    pji = path + os.path.basename(fastqfile1).split('_')[0] # pji = path + job_id 

    gf1= gzip.open(fastqfile1,'r') 
    gf2= gzip.open(os.path.join(pji + '_2.recal.fastq.gz'),'r') 

    output1=gzip.open(os.path.join(pji + '_1.fastq.gz'),'w') 
    output2=gzip.open(os.path.join(pji + '_2.fastq.gz'),'w') 

    for lines1,lines2 in gen(gf1,gf2): 
     output1.writelines(lines1) 
     output2.writelines(lines2) 

    output1.close() 
    output2.close() 

它应该减少30%的执行时间。纯粹的猜测。

PS:

代码

if sum(imap(ord,a[3]))/float(len(a[3])) >= 20 \ 
    and sum(imap(ord,b[3]))/float(len(b[3])) >= 20: 

更快速地执行,而不是

ave1 = sum(imap(ord,a[3]))/float(len(a[3])) 
ave2 = sum(imap(ord,b[3]))/float(len(b[3])) 
if ave1 >= 20 and ave2 >=20: 

因为如果AVE1不大于20,该对象AVE2 ISN” t评估。