2015-04-10 11 views
5

Sto cercando di leggere alcune righe specifiche di un file csv di grandi dimensioni e non voglio caricare l'intero file in memoria. L'indice delle righe specifiche saranno riportate in una lista L = [2, 5, 15, 98, ...] e il mio file CSV è simile al seguente:Come leggere righe specifiche di un file CSV di grandi dimensioni

Col 1, Col 2, Col3 
row11, row12, row13 
row21, row22, row23 
row31, row32, row33 
... 

Utilizzando le idee menzionate here Io uso il seguente comando per leggere le righe

with open('~/file.csv') as f: 
    r = csv.DictReader(f) # I need to read it as a dictionary for my purpose 

    for i in L: 
     for row in enumerate(r): 
      print row[i] 

ho subito il seguente errore:

IndexError        Traceback (most recent call last) 
<ipython-input-25-78951a0d4937> in <module>() 
     6  for i in L: 
     7   for row in enumerate(r): 
----> 8    print row[i] 
IndexError: tuple index out of range 

domanda 1. sembra che il mio uso delI cicliqui sono ovviamente sbagliati. Qualche idea su come risolvere questo problema?

D'altra parte, il seguente ottiene il lavoro fatto, ma è troppo lento:

def read_csv_line(line_number): 
    with open("~/file.csv") as f: 
     r = csv.DictReader(f) 
     for i, line in enumerate(r): 
      if i == (line_number - 2): 
       return line 
    return None 

for i in L: 
    print read_csv_line(i) 

Domanda 2. Qualche idea su come migliorare questo metodo di base di passare attraverso l'intero file fino a quando ho raggiungere la riga quindi stamparlo?

risposta

5

Un file non ha "linee" o "righe". Quello che consideri una "linea" è "ciò che si trova tra due newline caratteri". In quanto tale, non è possibile leggere l'ennesima riga senza leggere le righe precedenti, poiché non è possibile contare i caratteri di nuova riga.

Risposta 1: se si considera il vostro esempio, ma con L = [9], srotolando i loop darebbe:

i=9 
row = (0, {'Col 2': 'row12', 'Col 3': 'row13', 'Col 1': 'row11'}) 

Come si può vedere, riga è una tupla con due membri, chiamando row[i] significa row[9], quindi l'errore IndexError.

Risposta 2: Questo è molto lento perché si legge il file fino al numero di riga ogni volta. Nel tuo esempio, leggi le prime 2 righe, poi le prime 5, poi le prime 15, poi le prime 98, ecc. Quindi hai letto le prime 5 righe 3 volte. Si potrebbe creare un generatore che restituisce solo le righe che si desidera (attenzione, i numeri di riga sarebbero 0-indicizzato):

def read_my_lines(csv_reader, lines_list): 
    for line_number, row in enumerate(csv_reader): 
     if line_number in lines_list: 
      yield line_number, row 

Quindi, quando si desidera elaborare le linee, si dovrebbe fare:

L = [2, 5, 15, 98, ...] 
with open('~/file.csv') as f: 
    r = csv.DictReader(f) 
    for line_number, line in read_my_lines(r, L): 
     do_something_with_line(line) 

* Modifica *

Questo potrebbe essere ulteriormente migliorato per smettere di leggere il file quando avete letto tutte le linee che volevi:

def read_my_lines(csv_reader, lines_list): 
    # make sure every line number shows up only once: 
    lines_set = set(lines_list) 
    for line_number, row in enumerate(csv_reader): 
     if line_number in lines_set: 
      yield line_number, row 
      lines_set.remove(line_number) 
      # Stop when the set is empty 
      if not lines_set: 
       raise StopIteration 
2
for row in enumerate(r): 

tirerà le tuple. Stai quindi cercando di selezionare il tuo elemento ith da una tupla di 2 elementi.

ad esempio

>> for i in enumerate({"a":1, "b":2}): print i 
(0, 'a') 
(1, 'b') 

Inoltre, dal momento che i dizionari sono tabelle hash, l'ordine iniziale non è necessariamente conservato. per esempio:

>>list({"a":1, "b":2, "c":3, "d":5}) 
['a', 'c', 'b', 'd'] 
2

Supponendo L è una lista contenente i numeri di riga che si desidera, si potrebbe fare:

with open("~/file.csv") as f: 
    r = csv.DictReader(f) 
    for i, line in enumerate(r): 
     if i in L: # or (i+2) in L: from your second example 
      print line 

questo modo:

  • di leggere il file solo una volta
  • non si carica l'intero file nella memoria
  • si ottiene solo la lin es siete interessati a

L'unica avvertenza è che si legge tutto il file anche se L = [3]

+0

Grazie, alla fine ho finito per fare qualcosa di simile a questo. Il problema è che la mia lista L era già stata ordinata e non avevo davvero bisogno di controllare l'iscrizione ogni volta. Ho solo controllato se 'i == L [0]', e poi ho omesso la prima voce di 'L'. Per evitare di leggere l'intero file @vlad ha fornito una soluzione, sopra. – Keivan

1

Solo per riassumere le grandi idee, ho finito per usare qualcosa di simile: L può essere ordinato in tempi relativamente brevi, e nel mio caso è stato effettivamente già ordinato. Quindi, invece di vari controlli di appartenenza in L, si paga per ordinarlo e quindi controllare solo ogni indice contro la prima voce di esso. Qui è il mio pezzo di codice:

count=0 
with open('~/file.csv') as f: 
    r = csv.DictReader(f) 
    for row in r: 
     count += 1 
     if L == []: 
      break 
     elif count == L[0]: 
      print (row) 
      L.pop(0) 

Si noti che questo si ferma non appena abbiamo passato L una volta.

Problemi correlati