2010-05-23 18 views
18

Ho un oggetto csv DictReader (utilizzando Python 3.1), ma mi piacerebbe conoscere il numero di righe/righe contenute nel lettore prima di I iterate attraverso di esso. Qualcosa di simile a quanto segue ...Numero di righe in csv.DictReader

myreader = csv.DictReader(open('myFile.csv', newline='')) 

totalrows = ? 

rowcount = 0 
for row in myreader: 
    rowcount +=1 
    print("Row %d/%d" % (rowcount,totalrows)) 

so che potrei ottenere il totale scorrendo il lettore, ma poi non ho potuto correre la 'per' ciclo. Potrei scorrere una copia del lettore, ma non riesco a trovare come copiare un iteratore.

Potrei anche usare

totalrows = len(open('myFile.csv').readlines()) 

ma che sembra un inutile riapertura del file. Preferirei ottenere il conteggio da DictReader se possibile.

Qualsiasi aiuto sarebbe apprezzato.

Alan

risposta

22
rows = list(myreader) 
totalrows = len(rows) 
for i, row in enumerate(rows): 
    print("Row %d/%d" % (i+1, totalrows)) 
+0

Soluzione piacevole - Sono abbastanza nuovo all'idea degli iteratori, quindi non avevo davvero apprezzato enumerate() fino ad ora. Saluti. –

+7

Basta fare attenzione alle dimensioni del set di dati qui. Trasformare il tuo lettore in un elenco potrebbe richiedere GOBS di memoria. –

+1

Carica tutti i dati in memoria, le linee di conteggio -1 sono soluzioni molto carine –

2

non riesco a trovare il modo di copiare un iteratore.

vicina è itertools.tee, ma semplicemente facendo una list di esso, come suggerisce @JFSebastian, è meglio qui, come di itertools.tee documenti spiegano:

Questo itertools può richiedere significativo memoria ausiliaria (a seconda di come devono essere memorizzati più dati temporanei memorizzati). In generale, se un iteratore utilizza la maggior parte o tutti i dati prima dello un altro iteratore, è più veloce utilizzare list() anziché tee().

+0

Hai ancora il consumo di risorse potenzialmente elevato con entrambi i metodi. –

+0

Grazie Alex - elenco è allora. –

12

Hai solo bisogno di aprire il file una volta:

import csv 

f = open('myFile.csv', 'rb') 

countrdr = csv.DictReader(f) 
totalrows = 0 
for row in countrdr: 
    totalrows += 1 

f.seek(0) # You may not have to do this, I didn't check to see if DictReader did 

myreader = csv.DictReader(f) 
for row in myreader: 
    do_work 

Non importa quello che devi fare due passaggi (beh, se i record sono una lunghezza fissa - che è improbabile - si potrebbe solo ottenere la dimensione del file e dividere, ma lascia presumere che non è il caso). Aprire di nuovo il file non ti costa molto, ma puoi evitarlo come illustrato qui. La conversione in un elenco solo per utilizzare len() sta potenzialmente per sprecare tonnellate di memoria e non essere più veloce.

Nota: Il modo in cui 'Pythonic' è quello di utilizzare al posto di enumerate+=, ma il codice operativo UNPACK_TUPLE è così costoso che rende enumerate più lento di incrementare un locale. Detto questo, è probabile una micro-ottimizzazione non necessaria che probabilmente dovresti evitare.

Altre note: Se si desidera generare solo una sorta di indicatore di avanzamento, non deve necessariamente essere basato su record. È possibile tell() sull'oggetto file nel ciclo e basta segnalare la percentuale dei dati che si sta attraversando. Sarà un po 'irregolare, ma è probabile che su qualsiasi file sia abbastanza grande da giustificare una barra di progresso, la deviazione sulla lunghezza del record andrà persa nel rumore.

+0

Nick - grazie per la risposta. Sembra che la mia elusione di riaprire il file non valga il codice aggiuntivo in questione (la leggibilità è al di sopra delle prestazioni in questo caso). Grazie per il suggerimento riguardo alla velocità enumerate(). Tell() è anche una novità per me - cercherò di approfondire ulteriormente. Saluti. –

+0

Unico problema con questo ... e se si utilizza un vapore. – Nick

+0

@Nick: non c'è magia nel mondo - questo non è un problema, è solo un dato di fatto. –

Problemi correlati