python - Lectura de un enorme archivo.csv
python-2.7 (7)
Actualmente estoy tratando de leer datos de archivos .csv en Python 2.7 con hasta 1 millón de filas y 200 columnas (los archivos van desde 100 mb a 1.6 gb). Puedo hacer esto (muy lentamente) para los archivos con menos de 300,000 filas, pero una vez que voy más arriba obtengo errores de memoria. Mi código se ve así:
def getdata(filename, criteria):
data=[]
for criterion in criteria:
data.append(getstuff(filename, criteron))
return data
def getstuff(filename, criterion):
import csv
data=[]
with open(filename, "rb") as csvfile:
datareader=csv.reader(csvfile)
for row in datareader:
if row[3]=="column header":
data.append(row)
elif len(data)<2 and row[3]!=criterion:
pass
elif row[3]==criterion:
data.append(row)
else:
return data
El motivo de la cláusula else en la función getstuff es que todos los elementos que se ajustan al criterio se enumerarán juntos en el archivo csv, de modo que dejo el ciclo cuando los paso para ahorrar tiempo.
Mis preguntas son:
¿Cómo puedo lograr que esto funcione con los archivos más grandes?
¿Hay alguna manera de que pueda hacerlo más rápido?
Mi computadora tiene 8 GB de RAM, con Windows 7 de 64 bits, y el procesador es de 3,40 GHz (no estoy seguro de qué información necesita).
¡Muchas gracias por la ayuda!
Aunque la respuesta de Martijin es probablemente la mejor. Esta es una forma más intuitiva de procesar grandes archivos csv para principiantes. Esto le permite procesar grupos de filas o fragmentos a la vez.
import pandas as pd
chunksize = 10 ** 8
for chunk in pd.read_csv(filename, chunksize=chunksize):
process(chunk)
Está leyendo todas las filas en una lista y luego procesando esa lista. No hagas eso .
Procese sus filas a medida que las produce. Si necesita filtrar los datos primero, use una función de generador:
import csv
def getstuff(filename, criterion):
with open(filename, "rb") as csvfile:
datareader = csv.reader(csvfile)
yield next(datareader) # yield the header row
count = 0
for row in datareader:
if row[3] == criterion:
yield row
count += 1
elif count:
# done when having read a consecutive series of rows
return
También simplifiqué tu prueba de filtro; la lógica es la misma pero más concisa.
Como solo está haciendo coincidir una secuencia de filas que coincida con el criterio, también puede usar:
import csv
from itertools import dropwhile, takewhile
def getstuff(filename, criterion):
with open(filename, "rb") as csvfile:
datareader = csv.reader(csvfile)
yield next(datareader) # yield the header row
# first row, plus any subsequent rows that match, then stop
# reading altogether
# Python 2: use `for row in takewhile(...): yield row` instead
# instead of `yield from takewhile(...)`.
yield from takewhile(
lambda r: r[3] == criterion,
dropwhile(lambda r: r[3] != criterion, datareader))
return
Ahora puede pasar getstuff()
encima de getstuff()
directamente. Haz lo mismo en getdata()
:
def getdata(filename, criteria):
for criterion in criteria:
for row in getstuff(filename, criterion):
yield row
Ahora getdata()
bucle directamente sobre getdata()
en tu código:
for row in getdata(somefilename, sequence_of_criteria):
# process row
Ahora solo tiene una fila en la memoria, en lugar de miles de líneas por criterio.
yield
hace que una función sea una función de generador , lo que significa que no funcionará hasta que comiences a recorrerla.
Generator es una buena solución. Y en realidad, puede agregar un tiempo True: en el código (antes de abrir un csv), haciéndolo iterable para infinitos bucles.
Por ejemplo, en el conjunto de datos mnist:
import gzip
import numpy as np
BATCHSIZE = 100
DATASIZE = 1000
LOOP = DATASIZE//BATCHSIZE
def mnist_generator_iterable(path, name=''train_data''):
if name==''train_data'':
while True:
with gzip.open(path+''//train-images-idx3-ubyte.gz'') as bytestream:
bytestream.read(16)
for i in range(LOOP):
buf = bytestream.read(28*28*BATCHSIZE*1)
trainset = np.frombuffer(buf, dtype=np.uint8).astype(np.float32)
yield trainset.reshape([-1,784])
Hace poco estuve tratando de resolver el mismo problema, pero encontré que el paquete python pandas era razonablemente eficiente.
Es posible que desee comprobar aquí, http://pandas.pydata.org/
Pandas es una biblioteca de análisis de datos de alto rendimiento para Big Data.
Hago una buena cantidad de análisis de vibración y veo grandes conjuntos de datos (decenas y cientos de millones de puntos). Mi prueba mostró que la función pandas.read_csv() es 20 veces más rápida que numpy.genfromtxt (). Y la función genfromtxt () es 3 veces más rápida que numpy.loadtxt (). Parece que necesitas pandas para grandes conjuntos de datos.
Publiqué el código y los conjuntos de datos que utilicé en esta prueba en un blog sobre MATLAB vs Python para el análisis de vibraciones .
aquí hay otra solución para Python3:
import csv
with open(filename, "r") as csvfile:
datareader = csv.reader(csvfile)
count = 0
for row in datareader:
if row[3] in ("column header", criterion):
doSomething(row)
count += 1
elif count > 2:
break
aquí el datareader
es una función de generador.
lo que funcionó para mí fue y es superrápido
import pandas as pd
import dask.dataframe as dd
import time
t=time.clock()
df_train = dd.read_csv(''../data/train.csv'', usecols=[col1, col2])
df_train=df_train.compute()
print("load train: " , time.clock()-t)