2011-08-24 7 views
3

나는 초대형 txt 입력 파일을 다룰 필요가있다. 나는 보통 .readlines()를 사용하여 먼저 전체 파일을 읽고 그것을 목록으로 바꾼다. 나는 시도하고 일반적으로readlines() 사용을 피하는 방법은 무엇입니까?

#!/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() 

:

나는 정말 메모리 비용이고 매우 느릴 수 있습니다 알고 있지만, 나는 또한, 특정 라인을 조작 할 수 LIST 특성을 활용 아래와 같이 필요 전체 텍스트의 4 번째 줄을 읽지 만, 4 번째 줄이 원하는 조건을 충족하면이 4 줄을 텍스트에 추가합니다. 이렇게하려면 readlines()를 피할 수 있습니까? 들으

편집 : 안녕하세요, 실제로 나 자신이 더 나은 방법을 발견 : 내가 생각

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

'을 zcat은'슈퍼 빠른 .... 그것은 readlines 메쏘드에 15 분 주위했다입니다 1 분 동안 단지 zcat에 ...

답변

6

당신이 선형 적으로 파일을 읽을 수있는 코드를 리팩토링 수 있다면, 당신은 그냥 for line in file을 말할 수있다 한 번에 모든 파일을 메모리에 읽어 들이지 않고 파일의 각 행을 반복합니다. 그러나 파일 액세스가 더 복잡해 보이므로 생성자을 사용하여 readlines()을 대체 할 수 있습니다.이렇게하는 한 가지 방법은 사용하는 것입니다 itertools.izip 또는 itertools.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 

당신이에서이 개 파일을 읽는 경우 (라인의 동일한 번호) 시간이처럼 수행

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

간단한 방법이 될 것으로,

내가 L1과 L2의 obtention 개선이 충분이 아니라고 생각

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

(의사는 설명 목적으로 만, 오류가있을 수 있습니다) : 당신은 세계적으로

내가 제안 코드를 개선해야한다 :

#!/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 개체에게, ave2 ISN '20보다 크면 때문에 평가.

관련 문제