2011-02-10 16 views
14

W podstawowym miałem następny proces.Jak podzielić plik csv na porcje o równej wielkości w Pythonie?

import csv 
reader = csv.reader(open('huge_file.csv', 'rb')) 

for line in reader: 
    process_line(line) 

Zobacz ten powiązany question. Chcę wysłać linię procesową co 100 wierszy, aby zaimplementować podział partii.

Problem z zaimplementowaniem powiązanej odpowiedzi polega na tym, że obiekt csv jest nieobsługiwany i nie może używać len.

>>> import csv 
>>> reader = csv.reader(open('dataimport/tests/financial_sample.csv', 'rb')) 
>>> len(reader) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: object of type '_csv.reader' has no len() 
>>> reader[10:] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: '_csv.reader' object is unsubscriptable 
>>> reader[10] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: '_csv.reader' object is unsubscriptable 

Jak mogę rozwiązać ten problem?

Odpowiedz

20

Po prostu zmień swój reader, zawijając go w list. Oczywiście to pęknie na naprawdę dużych plików (Zobacz alternatywy w Aktualizacje poniżej):

>>> reader = csv.reader(open('big.csv', 'rb')) 
>>> lines = list(reader) 
>>> print lines[:100] 
... 

Dalsze czytanie: How do you split a list into evenly sized chunks in Python?


Update 1 (wersja lista): Innym możliwym sposobem byłoby po prostu przetwórz każdy uchwyt, który pojawia się podczas iteracji po liniach:

#!/usr/bin/env python 

import csv 
reader = csv.reader(open('4956984.csv', 'rb')) 

chunk, chunksize = [], 100 

def process_chunk(chuck): 
    print len(chuck) 
    # do something useful ... 

for i, line in enumerate(reader): 
    if (i % chunksize == 0 and i > 0): 
     process_chunk(chunk) 
     del chunk[:] 
    chunk.append(line) 

# process the remainder 
process_chunk(chunk) 

Aktualizacja 2 (wersja generatora): Nie odwzorować, ale może można zwiększyć wydajność poprzez zastosowanie kawał generator:

#!/usr/bin/env python 

import csv 
reader = csv.reader(open('4956984.csv', 'rb')) 

def gen_chunks(reader, chunksize=100): 
    """ 
    Chunk generator. Take a CSV `reader` and yield 
    `chunksize` sized slices. 
    """ 
    chunk = [] 
    for i, line in enumerate(reader): 
     if (i % chunksize == 0 and i > 0): 
      yield chunk 
      del chunk[:] 
     chunk.append(line) 
    yield chunk 

for chunk in gen_chunks(reader): 
    print chunk # process chunk 

# test gen_chunk on some dummy sequence: 
for chunk in gen_chunks(range(10), chunksize=3): 
    print chunk # process chunk 

# => yields 
# [0, 1, 2] 
# [3, 4, 5] 
# [6, 7, 8] 
# [9] 
+0

Problem polega na tym, że subskrybowanie pliku, wymusza odczyt wszystkich wierszy pliku. Jest to bardzo duże użycie plików i pamięci, jeśli to zrobię. –

+0

Piękny, dzięki @TheMYYN :-) –

+1

@Mario: Dodano wersję generatora, która może być szybsza (ale nie miałem czasu, aby przetestować - być może robisz). – miku

1

nie ma dobry sposób aby to zrobić dla wszystkich plików .csv. Powinieneś być w stanie podzielić plik na porcje używając file.seek, aby pominąć sekcję pliku. Następnie musisz przeskanować jeden bajt naraz, aby znaleźć koniec wiersza. Można oddzielnie przetwarzać te dwie porcje. Coś jak poniższy (nietestowany) kod powinien zacząć.

file_one = open('foo.csv') 
file_two = open('foo.csv') 
file_two.seek(0, 2)  # seek to the end of the file 
sz = file_two.tell() # fetch the offset 
file_two.seek(sz/2) # seek back to the middle 
chr = '' 
while chr != '\n': 
    chr = file_two.read(1) 
# file_two is now positioned at the start of a record 
segment_one = csv.reader(file_one) 
segment_two = csv.reader(file_two) 

Nie jestem pewien, w jaki sposób można stwierdzić, że zakończyłeś przeszukiwanie segment_one. Jeśli masz kolumnę w pliku CSV, która jest identyfikatorem wiersza, możesz przerwać przetwarzanie segment_one po napotkaniu identyfikatora wiersza z pierwszego wiersza w segment_two.