2011-02-10 6 views
14

En una base tuve el siguiente proceso.¿Cómo se divide un archivo csv en fragmentos de tamaño uniforme en Python?

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

for line in reader: 
    process_line(line) 

Ver este relacionado question. Deseo enviar la línea de proceso cada 100 filas para implementar la división de lotes.

El problema con la implementación de la respuesta relacionada es que el objeto csv no se puede suscribir y no puede usar 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 

¿Cómo puedo solucionar esto?

Respuesta

20

Simplemente haga que su reader se pueda subscribir envolviéndolo en un list. Obviamente, esto va a romper en realidad archivos de gran tamaño (ver alternativas en las Actualizaciones a continuación):

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

Más información: How do you split a list into evenly sized chunks in Python?


Actualización 1 (versión lista): Otra posibilidad sería solo procesa cada plato, ya que llega mientras se itera sobre las líneas:

#!/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) 

Actualización 2 (versión generador): No he referenciado, pero tal vez usted puede aumentar el rendimiento mediante el uso de un trozo generador:

#!/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

El problema es que al suscribir el archivo, fuerza a leer todas las líneas del archivo. Este es un archivo muy grande y el uso de memoria aumenta mucho si lo hago. –

+0

Hermoso, gracias @TheMYYN :-) –

+1

@Mario: Se agregó una versión de generador, que podría ser más rápida (pero no tuve tiempo para probarla, tal vez lo haga). – miku

1

No es una buena manera para hacer esto para todos los archivos .csv. Debería poder dividir el archivo en fragmentos utilizando file.seek para omitir una sección del archivo. Luego debe escanear un byte a la vez para encontrar el final de la fila. El puede procesar los dos fragmentos de forma independiente. Algo como el siguiente código (no probado) debería ayudarlo a comenzar.

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) 

no estoy seguro de cómo se puede decir que haya terminado de atravesar segment_one. Si tiene una columna en el archivo CSV que es una Id. De fila, puede detener el procesamiento de segment_one cuando encuentre el Id. De fila de la primera fila en segment_two.

Cuestiones relacionadas