¿Pandas groupby con sum () en un archivo csv grande?

Tengo un archivo grande (19GB o más) que quiero cargar en la memoria para realizar una agregación en algunas columnas.

el archivo se ve así:

id, col1, col2, col3, 1 , 12 , 15 , 13 2 , 18 , 15 , 13 3 , 14 , 15 , 13 3 , 14 , 185 , 213 

observe que, estoy usando las columnas (id, col1) para la agregación después de cargarlas en el dataframe, observe también que estas claves pueden repetirse sucesivamente varias veces, como:

 3 , 14 , 15 , 13 3 , 14 , 185 , 213 

Para un archivo pequeño, el siguiente script puede hacer el trabajo

 import pandas as pd data = pd.read_csv("data_file", delimiter=",") data = data.reset_index(drop=True).groupby(["id","col1"], as_index=False).sum() 

Sin embargo, para un archivo grande, necesito usar chunksize al leer el archivo csv para limitar el número de filas cargadas en la memoria:

 import pandas as pd data = pd.read_csv("data_file", delimiter=",", chunksize=1000000) data = data.reset_index(drop=True).groupby(["id","col1"], as_index=False).sum() 

En este último caso, habrá un problema si las filas donde (id, col1) son similares se dividen en archivos diferentes. ¿Cómo puedo lidiar con eso?

EDITAR

Como lo señaló @EdChum, hay una solución potencial, que es no solo agregar los resultados de groupby a un nuevo csv y leerlo nuevamente y realizar la agregación nuevamente hasta que el tamaño de df no cambie.

Esto, sin embargo, tiene un peor escenario que no se maneja, es decir:

cuando todos los archivos (o la cantidad suficiente de archivos que la memoria no puede manejar) tienen la misma problemática similar (id, col1) al final. Esto hará que el sistema devuelva un error de memoria.

En primer lugar, puede elegir la lista de constantes únicas leyendo csv con usecols – usecols=['id', 'col1'] . Luego lea csv by chunks, concat chunks por subconjunto de id y groupby. mejor explico

Si es mejor usar la columna col1 , cambie las constants = df['col1'].unique().tolist() . constants = df['col1'].unique().tolist() . Depende de sus datos.

O puede leer solo una columna df = pd.read_csv(io.StringIO(temp), sep=",", usecols=['id']) , depende de sus datos.

 import pandas as pd import numpy as np import io #test data temp=u"""id,col1,col2,col3 1,13,15,14 1,13,15,14 1,12,15,13 2,18,15,13 2,18,15,13 2,18,15,13 2,18,15,13 2,18,15,13 2,18,15,13 3,14,15,13 3,14,15,13 3,14,185,213""" df = pd.read_csv(io.StringIO(temp), sep=",", usecols=['id', 'col1']) #drop duplicities, from out you can choose constant df = df.drop_duplicates() print df # id col1 #0 1 13 #2 1 12 #3 2 18 #9 3 14 #for example list of constants constants = [1,2,3] #or column id to list of unique values constants = df['id'].unique().tolist() print constants #[1L, 2L, 3L] for i in constants: iter_csv = pd.read_csv(io.StringIO(temp), delimiter=",", chunksize=10) #concat subset with rows id == constant df = pd.concat([chunk[chunk['id'] == i] for chunk in iter_csv]) #your groupby function data = df.reset_index(drop=True).groupby(["id","col1"], as_index=False).sum() print data.to_csv(index=False) #id,col1,col2,col3 #1,12,15,13 #1,13,30,28 # #id,col1,col2,col3 #2,18,90,78 # #id,col1,col2,col3 #3,14,215,239 

solución dask

Dask.dataframe casi puede hacer esto sin modificación

 $ cat so.csv id,col1,col2,col3 1,13,15,14 1,13,15,14 1,12,15,13 2,18,15,13 2,18,15,13 2,18,15,13 2,18,15,13 2,18,15,13 2,18,15,13 3,14,15,13 3,14,15,13 3,14,185,213 $ pip install dask[dataframe] $ ipython In [1]: import dask.dataframe as dd In [2]: df = dd.read_csv('so.csv', sep=',') In [3]: df.head() Out[3]: id col1 col2 col3 0 1 13 15 14 1 1 13 15 14 2 1 12 15 13 3 2 18 15 13 4 2 18 15 13 In [4]: df.groupby(['id', 'col1']).sum().compute() Out[4]: col2 col3 id col1 1 12 15 13 13 30 28 2 18 90 78 3 14 215 239 

Nadie ha escrito as_index=False para groupby sin embargo. Podemos solucionar este problema con assign .

 In [5]: df.assign(id_2=df.id, col1_2=df.col1).groupby(['id_2', 'col1_2']).sum().compute() Out[5]: id col1 col2 col3 id_2 col1_2 1 12 1 12 15 13 13 2 26 30 28 2 18 12 108 90 78 3 14 9 42 215 239 

Como funciona esto

Sacaremos trozos y haremos groupbys como en tu primer ejemplo. Una vez que hayamos terminado de agrupar y sumr cada uno de los fragmentos, reuniremos todos los resultados intermedios juntos y haremos otro groupby.sum ligeramente diferente. Esto supone que los resultados intermedios cabrán en la memoria.

Paralelismo

Como efecto secundario agradable, esto también funcionará en paralelo.