2010-02-24 15 views
11

Ho due grandi file di testo (~ 100 GB) che devono essere ripetuti simultaneamente.zip() alternativa per iterare attraverso due iterables

Zip funziona bene per i file più piccoli, ma ho scoperto che in realtà sta facendo un elenco di linee dai miei due file. Ciò significa che ogni riga viene archiviata nella memoria. Non ho bisogno di fare nulla con le linee più di una volta.

handle1 = open('filea', 'r'); handle2 = open('fileb', 'r') 

for i, j in zip(handle1, handle2): 
    do something with i and j. 
    write to an output file. 
    no need to do anything with i and j after this. 

Esiste un'alternativa a zip() che agisce come un generatore che mi permetterà di scorrere questi due file senza l'utilizzo> 200 GB di ram?

+0

... in realtà, so di un modo, ma non sembra molto pitone - while line1: line1 = handle1.readline(); line2 = handle2.readline(); fai qualcosa con line1 e line2 ... –

+0

Parlando di ambienti con limiti di memoria potresti trovare questo interessante http://neopythonic.blogspot.com/2008/10/sorting-million-32-bit-integers-in-2mb.html –

risposta

20

itertools ha una funzione izip che lo fa

from itertools import izip 
for i, j in izip(handle1, handle2): 
    ... 

Se i file sono di dimensioni diverse è possibile utilizzare izip_longest, come izip si fermerà al file più piccolo.

-1

Qualcosa di simile? Wordy, ma sembra essere quello che stai chiedendo.

Può essere regolato per fare cose come una fusione adeguata per abbinare le chiavi tra i due file, che è spesso più ciò che è necessario rispetto alla semplice funzione di zip. Inoltre, questo non troncare, che è ciò che l'algoritmo SQL OUTER JOIN fa, ancora una volta, diverso da ciò che fa zip e più tipico dei file.

with open("file1","r") as file1: 
    with open("file2", "r" as file2: 
     for line1, line2 in parallel(file1, file2): 
      process lines 

def parallel(file1, file2): 
    if1_more, if2_more = True, True 
    while if1_more or if2_more: 
     line1, line2 = None, None # Assume simplistic zip-style matching 
     # If you're going to compare keys, then you'd do that before 
     # deciding what to read. 
     if if1_more: 
      try: 
       line1= file1.next() 
      except StopIteration: 
       if1_more= False 
     if if2_more: 
      try: 
       line2= file2.next() 
      except StopIteration: 
       if2_more= False 
     yield line1, line2 
+3

Non intendevi 'while if1_more OR if2_more:'? E perché avvolgere file1 e file2 in iter, quando i file sono già iter? E infine, era solo un accademico "come farei questo per me stesso, se dovessi?" esercizio? Sicuramente si preferirebbe usare izip o izip_longest dal modulo itertools nella std lib, invece di scrivere 20 righe di codice homebrewed che fa la stessa cosa, ma dovrebbe essere mantenuta e supportata (e debugata!). – PaulMcG

+0

@Paul McGuire: Sì, O è corretto.L'iter esplicito è richiesto per l'utilizzo successivo e ottiene un'eccezione StopIteraction corretta in EOF. No, questo non era "accademico". Questa è una risposta alla domanda. La domanda è vaga e itertools potrebbe non fornire le funzionalità richieste. Questo non può neanche, ma questo può essere adattato. –

+0

Sto eseguendo Py2.5.4 e chiamando 'next()' su un oggetto file alla fine del file solleva StopIteration per me. – PaulMcG

0

Se si desidera troncare il file più breve:

handle1 = open('filea', 'r') 
handle2 = open('fileb', 'r') 

try: 
    while 1: 
     i = handle1.next() 
     j = handle2.next() 

     do something with i and j. 
     write to an output file. 

except StopIteration: 
    pass 

finally: 
    handle1.close() 
    handle2.close() 

Else

handle1 = open('filea', 'r') 
handle2 = open('fileb', 'r') 

i_ended = False 
j_ended = False 
while 1: 
    try: 
     i = handle1.next() 
    except StopIteration: 
     i_ended = True 
    try: 
     j = handle2.next() 
    except StopIteration: 
     j_ended = True 

     do something with i and j. 
     write to an output file. 
    if i_ended and j_ended: 
     break 

handle1.close() 
handle2.close() 

O

handle1 = open('filea', 'r') 
handle2 = open('fileb', 'r') 

while 1: 
    i = handle1.readline() 
    j = handle2.readline() 

    do something with i and j. 
    write to an output file. 

    if not i and not j: 
     break 
handle1.close() 
handle2.close() 
+0

E se i due file hanno lunghezze diverse? Questo troncerà a quello più corto. Spero che questo sia il comportamento desiderato. –

+0

@ S.Lott: non è questo il significato di 'zip'? – voyager

+0

@ S.Lott - questo interrompe solo il ciclo while-forever quando entrambi i_ended E j_ended, quindi leggerà fino alla fine del file più lungo. Ma c'è sicuramente spazio per miglioramenti. Se un file è molto più corto dell'altro, il codice corrente chiamerà .next() e catturerà StopIteration * molte * volte, quando abbiamo già appreso che il file è terminato. Abbastanza semplice da fare: 'if not i_ended: try: i = handel1.next() ...' (come fai nel tuo codice 'if if1_more: '). (Ah, vedo che il tuo commento stava rispondendo al codice originale, non alla versione modificata - scusami per il collegamento!) – PaulMcG

14

È possibile utilizzare izip_longest come questo per padil file più breve con righe vuote

in pitone 2,6

from itertools import izip_longest 
with handle1 as open('filea', 'r'): 
    with handle2 as open('fileb', 'r'): 
     for i, j in izip_longest(handle1, handle2, fillvalue=""): 
      ... 

o in python3.1

from itertools import izip_longest 
with handle1 as open('filea', 'r'), handle2 as open('fileb', 'r'): 
    for i, j in izip_longest(handle1, handle2, fillvalue=""): 
     ... 
+0

+1 per 'with' - Mi piace la sintassi Py3.1 per mantenere bassi i livelli di rientro. – PaulMcG

0

Per python3, izip_longest è in realtà zip_longest.

from itertools import zip_longest 

for i, j in izip(handle1, handle2): 
    ... 
Problemi correlati