Cómo iterar sobre trozos consecutivos del dataframe de Pandas de manera eficiente

Tengo un gran dataframe (varios millones de filas).

Quiero poder realizar una operación grupal en él, pero simplemente agrupándolos por subconjuntos consecutivos (preferiblemente de igual tamaño) de filas, en lugar de usar cualquier propiedad particular de las filas individuales para decidir a qué grupo van.

El caso de uso: quiero aplicar una función a cada fila a través de un mapa paralelo en IPython. No importa qué filas van a qué motor de back-end, ya que la función calcula un resultado basado en una fila a la vez. (Conceptualmente al menos; en realidad está vectorizado).

Se me ha ocurrido algo como esto:

# Generate a number from 0-9 for each row, indicating which tenth of the DF it belongs to max_idx = dataframe.index.max() tenths = ((10 * dataframe.index) / (1 + max_idx)).astype(np.uint32) # Use this value to perform a groupby, yielding 10 consecutive chunks groups = [g[1] for g in dataframe.groupby(tenths)] # Process chunks in parallel results = dview.map_sync(my_function, groups) 

Pero esto parece muy largo, y no garantiza trozos de igual tamaño. Especialmente si el índice es escaso o no entero o lo que sea.

¿Alguna sugerencia para una mejor manera?

¡Gracias!

En la práctica, no puede garantizar trozos de igual tamaño: después de todo, la cantidad de filas podría ser primordial, en cuyo caso, sus únicas opciones de trozos serían trozos de tamaño 1 o uno grande. groupby a pasar una matriz a groupby . Empezando desde:

 >>> df = pd.DataFrame(np.random.rand(15, 5), index=[0]*15) >>> df[0] = range(15) >>> df 0 1 2 3 4 0 0 0.746300 0.346277 0.220362 0.172680 0 1 0.657324 0.687169 0.384196 0.214118 0 2 0.016062 0.858784 0.236364 0.963389 [...] 0 13 0.510273 0.051608 0.230402 0.756921 0 14 0.950544 0.576539 0.642602 0.907850 [15 rows x 5 columns] 

donde deliberadamente he hecho que el índice no sea informativo al establecerlo en 0, simplemente decidimos nuestro tamaño (aquí 10) y dividimos con enteros una matriz por este:

 >>> df.groupby(np.arange(len(df))//10)  >>> for k,g in df.groupby(np.arange(len(df))//10): ... print(k,g) ... 0 0 1 2 3 4 0 0 0.746300 0.346277 0.220362 0.172680 0 1 0.657324 0.687169 0.384196 0.214118 0 2 0.016062 0.858784 0.236364 0.963389 [...] 0 8 0.241049 0.246149 0.241935 0.563428 0 9 0.493819 0.918858 0.193236 0.266257 [10 rows x 5 columns] 1 0 1 2 3 4 0 10 0.037693 0.370789 0.369117 0.401041 0 11 0.721843 0.862295 0.671733 0.605006 [...] 0 14 0.950544 0.576539 0.642602 0.907850 [5 rows x 5 columns] 

Los métodos basados ​​en el corte del DataFrame pueden fallar cuando el índice no es compatible con eso, aunque siempre puede usar .iloc[a:b] para ignorar los valores del índice y acceder a los datos por posición.

No estoy seguro de si esto es exactamente lo que desea, pero encontré estas funciones de agrupador en otro subproceso SO bastante útil para hacer un grupo de multiprocesadores.

Aquí hay un breve ejemplo de ese hilo, que podría hacer algo como lo que quieres:

 import numpy as np import pandas as pds df = pds.DataFrame(np.random.rand(14,4), columns=['a', 'b', 'c', 'd']) def chunker(seq, size): return (seq[pos:pos + size] for pos in xrange(0, len(seq), size)) for i in chunker(df,5): print i 

Lo que te da algo como esto:

  abcd 0 0.860574 0.059326 0.339192 0.786399 1 0.029196 0.395613 0.524240 0.380265 2 0.235759 0.164282 0.350042 0.877004 3 0.545394 0.881960 0.994079 0.721279 4 0.584504 0.648308 0.655147 0.511390 abcd 5 0.276160 0.982803 0.451825 0.845363 6 0.728453 0.246870 0.515770 0.343479 7 0.971947 0.278430 0.006910 0.888512 8 0.044888 0.875791 0.842361 0.890675 9 0.200563 0.246080 0.333202 0.574488 abcd 10 0.971125 0.106790 0.274001 0.960579 11 0.722224 0.575325 0.465267 0.258976 12 0.574039 0.258625 0.469209 0.886768 13 0.915423 0.713076 0.073338 0.622967 

Espero que eso ayude.

EDITAR

En este caso, usé esta función con el conjunto de procesadores de (aproximadamente) de esta manera:

 from multiprocessing import Pool nprocs = 4 pool = Pool(nprocs) for chunk in chunker(df, nprocs): data = pool.map(myfunction, chunk) data.domorestuff() 

Supongo que esto debería ser muy similar al uso de la maquinaria distribuida de IPython, pero no lo he probado.

Use numpy tiene esto incorporado: np.array_split ()

 import numpy as np import pandas as pd data = pd.DataFrame(np.random.rand(10, 3)) for chunk in np.array_split(data, 5): assert len(chunk) == len(data) / 5 

Un signo de un buen ambiente es muchas opciones, así que agregaré esto de Anaconda Blaze , realmente usando Odo

 import blaze as bz import pandas as pd df = pd.DataFrame({'col1':[1,2,3,4,5], 'col2':[2,4,6,8,10]}) for chunk in bz.odo(df, target=bz.chunks(pd.DataFrame), chunksize=2): # Do stuff with chunked dataframe