Как избежать использования readlines ()? - PullRequest
3 голосов
/ 24 августа 2011

Мне нужно иметь дело с очень большими входными txt-файлами, и я обычно использую .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-ю строку всего текста, но если 4-я строка соответствует требуемому условию, я добавлю эти 4 строки в текст. Так можно ли избежать readlines () для достижения этой цели? ТНХ

EDIT: Привет, на самом деле я сам нашел лучший способ:

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

Я думаю, что 'zcat' супер быстрый .... Чтение строк заняло около 15 минут, в то время как zcat - всего 1 минута ...

Ответы [ 6 ]

6 голосов
/ 24 августа 2011

Если вы можете реорганизовать свой код для линейного чтения файла, то вы можете просто сказать 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.
2 голосов
/ 24 августа 2011

Простой способ:

(псевдокод, может содержать ошибки, только в иллюстративных целях)

    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()
1 голос
/ 24 августа 2011

Вот как напечатать все строки, содержащие 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
1 голос
/ 24 августа 2011

Вы можете использовать enumerate для итерации по строкам в файле, что будет возвращать счетчик и строку для каждой итерации:

with open(file_name) as f:
    for i, line in enumerate(f):
        if i % 4 == 3:
            print i, line
0 голосов
/ 25 августа 2011

Я думаю, что улучшения обнаружения 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 не оценивается.

0 голосов
/ 24 августа 2011

Tricky, потому что у вас есть два файла, которые вы обрабатываете одновременно.

Вы можете использовать модуль 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()
...