2015-04-10 21 views
5

Próbuję odczytać określone wiersze dużego pliku CSV i nie chcę załadować całego pliku do pamięci. Indeks poszczególnych wierszach podane są na liście L = [2, 5, 15, 98, ...] i mój plik CSV wygląda tak:Jak odczytywać określone wiersze dużego pliku csv

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

Korzystanie pomysły wymienione here używam następujące polecenie, aby przeczytać wiersze

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] 

razu Pojawia się następujący błąd:

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 

Pytanie 1. wydaje się moim użytkowania for pętle tutaj jest oczywiście błędne. Wszelkie pomysły, jak to naprawić?

Z drugiej strony, co następuje dostaje zadanie, ale jest zbyt powolny:

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) 

Pytanie 2. Każdy pomysł, jak poprawić tę podstawową metodę przeżywa całego pliku, dopóki nie dotrzeć do rzędu, a następnie wydrukować?

Odpowiedz

5

Plik nie ma „linii” lub „wiersze”. To, co uważasz za "linię", to "to, co znajduje się pomiędzy dwoma znakami newline". W związku z tym nie można odczytać n-tej linii bez czytania linii przed nią, ponieważ nie można policzyć znaków nowej linii.

Odpowiedź 1: jeśli wziąć pod uwagę swój przykład, ale z L = [9], rozwijając swoje pętle dałby:

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

Jak widać, rząd jest krotką z dwóch członków, nazywając row[i] oznacza row[9], stąd błąd IndexError.

Odpowiedź 2: Jest to bardzo powolne, ponieważ za każdym razem czytasz plik do numeru wiersza. W twoim przykładzie przeczytałeś pierwsze 2 wiersze, potem pierwsze 5, potem pierwsze 15, potem pierwsze 98 itd. Więc przeczytałeś pierwsze 5 wierszy 3 razy. Można stworzyć generator, który zwraca tylko wiersze, które chcesz (uwaga, numery linii będzie 0-indeksowane):

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 

Więc jeśli chcesz przetwarzać linie, zrobiłbyś:

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) 

* Edit *

ten można jeszcze poprawić, aby zatrzymać odczytu pliku, gdy czytałeś wszystkie linie, które Poszukiwany

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): 

będzie ciągnąć krotki. Następnie próbujesz wybrać swój ith element z krotki 2 elementowej.

np

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

Dodatkowo, ponieważ słowniki są tabele mieszania, początkowy kolejność niekoniecznie jest zachowana. na przykład:

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

Zakładając L jest lista zawierająca numery linii, które chcesz, możesz zrobić:

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 

ten sposób:

  • czytasz plik tylko raz
  • nie ładujesz całego pliku do pamięci
  • masz tylko linę es jesteś zainteresowany

Jedynym zastrzeżeniem jest to, że można przeczytać cały plik nawet jeśli L = [3]

+0

Dzięki, w końcu skończyłem robić coś podobnego do tego. Połów był taki, że moja lista L została już zamówiona i nie musiałem za każdym razem sprawdzać członkostwa. Sprawdziłem tylko, czy 'i == L [0]', a następnie pominąłem pierwszy wpis 'L'. Aby uniknąć czytania cały plik @vlad podał rozwiązanie, powyżej. – Keivan

1

Po prostu podsumowując wspaniałe pomysły, skończyło się na użyciu czegoś takiego: L może być sortowane stosunkowo szybko, aw moim przypadku było już posortowane. Tak więc, zamiast kilku kontroli członkostwa w L opłaca się go posortować, a następnie sprawdzać tylko każdy indeks przed pierwszym wprowadzeniem do niego. Tu jest mój kawałek kodu:

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) 

Zauważ, że to przestaje jak tylko przeszliśmy przez L raz.

Powiązane problemy