Flujos de trabajo de "datos grandes" con pandas

1115

He intentado descifrar una respuesta a esta pregunta durante muchos meses mientras aprendía pandas. Utilizo SAS para mi trabajo diario y es excelente por su soporte fuera de núcleo. Sin embargo, SAS es horrible como software por muchas otras razones.

Algún día espero reemplazar mi uso de SAS con python y pandas, pero actualmente me falta un flujo de trabajo fuera del núcleo para grandes conjuntos de datos. No estoy hablando de "big data" que requiere una red distribuida, sino de archivos demasiado grandes para caber en la memoria pero lo suficientemente pequeños como para caber en un disco duro.

Mi primer pensamiento es utilizarlo HDFStorepara mantener grandes conjuntos de datos en el disco y extraer solo las piezas que necesito en marcos de datos para su análisis. Otros han mencionado MongoDB como una alternativa más fácil de usar. Mi pregunta es esta:

¿Cuáles son algunos de los flujos de trabajo de mejores prácticas para lograr lo siguiente?

  1. Carga de archivos planos en una estructura de base de datos permanente en disco
  2. Consultar esa base de datos para recuperar datos para alimentar una estructura de datos de pandas
  3. Actualización de la base de datos después de manipular piezas en pandas

Se agradecerían mucho los ejemplos del mundo real, especialmente de cualquiera que use pandas en "datos grandes".

Editar: un ejemplo de cómo me gustaría que esto funcione:

  1. Importe de forma iterativa un archivo plano grande y guárdelo en una estructura de base de datos permanente en disco. Estos archivos suelen ser demasiado grandes para caber en la memoria.
  2. Para usar Pandas, me gustaría leer subconjuntos de estos datos (generalmente solo unas pocas columnas a la vez) que pueden caber en la memoria.
  3. Crearía nuevas columnas realizando varias operaciones en las columnas seleccionadas.
  4. Entonces tendría que agregar estas nuevas columnas a la estructura de la base de datos.

Estoy tratando de encontrar una forma de mejores prácticas para realizar estos pasos. Al leer enlaces sobre pandas y pytables, parece que agregar una nueva columna podría ser un problema.

Editar: respondiendo específicamente a las preguntas de Jeff:

  1. Estoy construyendo modelos de riesgo crediticio al consumidor. Los tipos de datos incluyen características de teléfono, SSN y dirección; valores de propiedad; información despectiva como antecedentes penales, quiebras, etc ... Los conjuntos de datos que utilizo todos los días tienen cerca de 1.000 a 2.000 campos en promedio de tipos de datos mixtos: variables continuas, nominales y ordinales de datos numéricos y de caracteres. Rara vez agrego filas, pero realizo muchas operaciones que crean nuevas columnas.
  2. Las operaciones típicas implican combinar varias columnas usando lógica condicional en una nueva columna compuesta. Por ejemplo if var1 > 2 then newvar = 'A' elif var2 = 4 then newvar = 'B',. El resultado de estas operaciones es una nueva columna para cada registro en mi conjunto de datos.
  3. Finalmente, me gustaría agregar estas nuevas columnas a la estructura de datos en disco. Repetiría el paso 2, explorando los datos con tablas de referencias cruzadas y estadísticas descriptivas tratando de encontrar relaciones interesantes e intuitivas para modelar.
  4. Un archivo de proyecto típico suele ser de aproximadamente 1 GB. Los archivos están organizados de tal manera que una fila consiste en un registro de datos del consumidor. Cada fila tiene el mismo número de columnas para cada registro. Este será siempre el caso.
  5. Es bastante raro que haga subconjuntos por filas al crear una nueva columna. Sin embargo, es bastante común para mí crear subconjuntos en filas al crear informes o generar estadísticas descriptivas. Por ejemplo, es posible que desee crear una frecuencia simple para una línea de negocio específica, por ejemplo, tarjetas de crédito minoristas. Para hacer esto, seleccionaría solo aquellos registros donde la línea de negocio = minorista, además de las columnas sobre las que quiero informar. Sin embargo, al crear nuevas columnas, extraería todas las filas de datos y solo las columnas que necesito para las operaciones.
  6. El proceso de modelado requiere que analice cada columna, busque relaciones interesantes con alguna variable de resultado y cree nuevas columnas compuestas que describan esas relaciones. Las columnas que exploro generalmente se hacen en pequeños conjuntos. Por ejemplo, me enfocaré en un conjunto de, digamos, 20 columnas que solo tratan con valores de propiedad y observaré cómo se relacionan con el incumplimiento de un préstamo. Una vez que se exploran y se crean nuevas columnas, paso a otro grupo de columnas, digo educación universitaria y repito el proceso. Lo que estoy haciendo es crear variables candidatas que expliquen la relación entre mis datos y algún resultado. Al final de este proceso, aplico algunas técnicas de aprendizaje que crean una ecuación a partir de esas columnas compuestas.

Es raro que alguna vez agregue filas al conjunto de datos. Casi siempre estaré creando nuevas columnas (variables o características en estadísticas / lenguaje de aprendizaje automático).

6
  • 1
    ¿Es la relación tamaño de núcleo / tamaño completo 1%, 10%? ¿Importa? Si pudiera comprimir cols a int8, o filtrar filas ruidosas, ¿cambiaría eso su ciclo de cálculo-pensamiento de, digamos, horas a minutos? (También agregue etiquetas de datos grandes).
    denis
    18 de marzo de 2013 a las 11:26
  • 1
    Almacenar float32 en lugar de float64, e int8 donde sea posible, debería ser trivial (aunque no sé qué herramientas / funciones hacen float64 internamente)
    denis
    18 de marzo de 2013 a las 11:59
  • ¿Puede dividir su tarea en partes del trabajo? 8 dic 2017 a las 3:09
  • 1
    una buena solución de 2019 para hacer operaciones tipo pandas en datos "medianos" que no caben en la memoria es dask 30 oct 2019 a las 17:56
  • Hay alternativas a python + pandas que es posible que desee considerar ver ya que recién está comenzando. Considere el hecho de que Python es un lenguaje de programación de propósito general (no un DSL para el análisis y la manipulación de datos) y que pandas es una biblioteca agregada a eso. Consideraría mirar R o kdb. 16/11/19 a las 14:02
687
+50

Utilizo habitualmente decenas de gigabytes de datos de esta manera, por ejemplo, tengo tablas en el disco que leo a través de consultas, creo datos y anexo.

Vale la pena leer los documentos y más adelante en este hilo para obtener varias sugerencias sobre cómo almacenar sus datos.

Detalles que afectarán la forma en que almacena sus datos, como:
Proporcione todos los detalles que pueda; y puedo ayudarte a desarrollar una estructura.

  1. Tamaño de datos, # de filas, columnas, tipos de columnas; ¿está agregando filas o solo columnas?
  2. Cómo se verán las operaciones típicas. Por ejemplo, haga una consulta en columnas para seleccionar un montón de filas y columnas específicas, luego haga una operación (en memoria), cree nuevas columnas y guárdelas.
    (Dar un ejemplo de juguete podría permitirnos ofrecer recomendaciones más específicas).
  3. Después de ese procesamiento, ¿qué haces? ¿El paso 2 es ad hoc o repetible?
  4. Archivos planos de entrada: cuántos, tamaño total aproximado en Gb. ¿Cómo se organizan, por ejemplo, por registros? ¿Cada uno contiene diferentes campos o tienen algunos registros por archivo con todos los campos en cada archivo?
  5. ¿Alguna vez seleccionó subconjuntos de filas (registros) en función de criterios (por ejemplo, seleccione las filas con el campo A> 5)? y luego hacer algo, o simplemente selecciona los campos A, B, C con todos los registros (y luego hace algo)?
  6. ¿"Trabaja" en todas sus columnas (en grupos), o hay una buena proporción que solo puede usar para informes (por ejemplo, desea mantener los datos, pero no necesita extraer esa columna explícitamente hasta tiempo de resultados finales)?

Solución

Asegúrese de tener pandas al menos0.10.1 instalados.

Leer archivos iterativos fragmento a fragmento y consultas de varias tablas .

Dado que pytables está optimizado para operar en filas (que es lo que consulta), crearemos una tabla para cada grupo de campos. De esta manera, es fácil seleccionar un pequeño grupo de campos (que funcionarán con una tabla grande, pero es más eficiente hacerlo de esta manera ... creo que puedo solucionar esta limitación en el futuro ... esto es más intuitivo de todos modos):
(Lo siguiente es pseudocódigo).

import numpy as np
import pandas as pd

# create a store
store = pd.HDFStore('mystore.h5')

# this is the key to your storage:
#    this maps your fields to a specific group, and defines 
#    what you want to have as data_columns.
#    you might want to create a nice class wrapping this
#    (as you will want to have this map and its inversion)  
group_map = dict(
    A = dict(fields = ['field_1','field_2',.....], dc = ['field_1',....,'field_5']),
    B = dict(fields = ['field_10',......        ], dc = ['field_10']),
    .....
    REPORTING_ONLY = dict(fields = ['field_1000','field_1001',...], dc = []),

)

group_map_inverted = dict()
for g, v in group_map.items():
    group_map_inverted.update(dict([ (f,g) for f in v['fields'] ]))

Leer los archivos y crear el almacenamiento (esencialmente haciendo lo que append_to_multiplehace):

for f in files:
   # read in the file, additional options may be necessary here
   # the chunksize is not strictly necessary, you may be able to slurp each 
   # file into memory in which case just eliminate this part of the loop 
   # (you can also change chunksize if necessary)
   for chunk in pd.read_table(f, chunksize=50000):
       # we are going to append to each table by group
       # we are not going to create indexes at this time
       # but we *ARE* going to create (some) data_columns

       # figure out the field groupings
       for g, v in group_map.items():
             # create the frame for this group
             frame = chunk.reindex(columns = v['fields'], copy = False)    

             # append it
             store.append(g, frame, index=False, data_columns = v['dc'])

Ahora tiene todas las tablas en el archivo (en realidad, podría almacenarlas en archivos separados si lo desea, probablemente tendría que agregar el nombre del archivo al group_map, pero probablemente esto no sea necesario).

Así es como obtienes columnas y creas otras nuevas:

frame = store.select(group_that_I_want)
# you can optionally specify:
# columns = a list of the columns IN THAT GROUP (if you wanted to
#     select only say 3 out of the 20 columns in this sub-table)
# and a where clause if you want a subset of the rows

# do calculations on this frame
new_frame = cool_function_on_frame(frame)

# to 'add columns', create a new group (you probably want to
# limit the columns in this new_group to be only NEW ones
# (e.g. so you don't overlap from the other tables)
# add this info to the group_map
store.append(new_group, new_frame.reindex(columns = new_columns_created, copy = False), data_columns = new_columns_created)

Cuando esté listo para post_processing:

# This may be a bit tricky; and depends what you are actually doing.
# I may need to modify this function to be a bit more general:
report_data = store.select_as_multiple([groups_1,groups_2,.....], where =['field_1>0', 'field_1000=foo'], selector = group_1)

Acerca de data_columns, en realidad no es necesario definir NINGUNA data_columns; le permiten sub-seleccionar filas según la columna. Por ejemplo, algo como:

store.select(group, where = ['field_1000=foo', 'field_1001>0'])

Es posible que le resulten más interesantes en la etapa final de generación del informe (básicamente, una columna de datos está separada de otras columnas, lo que podría afectar la eficiencia de alguna manera si define mucho).

También es posible que desee:

  • cree una función que tome una lista de campos, busque los grupos en groups_map, luego los seleccione y concatene los resultados para obtener el marco resultante (esto es esencialmente lo que hace select_as_multiple). De esta manera, la estructura será bastante transparente para ti.
  • índices en ciertas columnas de datos (hace que el subconjunto de filas sea mucho más rápido).
  • habilitar la compresión.

¡Avísame cuando tengas preguntas!

17
  • 5
    Gracias por los enlaces. El segundo enlace me preocupa un poco de que no pueda agregar nuevas columnas a las tablas en HDFStore. ¿Es eso correcto? Además, agregué un ejemplo de cómo usaría esta configuración. 10 de enero de 2013 a las 23:53
  • 5
    La estructura real en el hdf depende de usted. Pytables está orientado a filas, con columnas fijas en el momento de la creación. No puede agregar columnas una vez que se crea una tabla. Sin embargo, puede crear una nueva tabla indexada igual que su tabla existente. (vea los ejemplos de select_as_multiple en los documentos). De esta manera, puede crear objetos de tamaño arbitrario mientras tiene consultas bastante eficientes. La forma en que utiliza los datos es clave para organizarlos en el disco. Envíeme un correo electrónico fuera de la lista con un pseudocódigo de un ejemplo más específico.
    Jeff
    11 de enero de 2013 a las 0:27
  • 1
    He actualizado mi pregunta para responder a sus puntos detallados. Trabajaré en un ejemplo para sacarte de la lista. ¡Gracias! 11 de enero de 2013 a las 4:29
  • 13
    @Jeff, con Pandas en 0.17.x ahora, ¿se han resuelto los problemas descritos anteriormente en Pandas? 13/01/16 a las 9:33
  • 9
    ¿@Jeff está interesado en agregar una actualización rápida de su respuesta para promover dask?
    Zeugma
    9 de septiembre de 2016 a las 18:10
163

Creo que a las respuestas anteriores les falta un enfoque simple que he encontrado muy útil.

Cuando tengo un archivo que es demasiado grande para cargarlo en la memoria, divido el archivo en varios archivos más pequeños (ya sea por filas o columnas)

Ejemplo: en el caso de 30 días de datos comerciales de ~ 30 GB de tamaño, los divido en un archivo por día de ~ 1 GB de tamaño. Posteriormente, proceso cada archivo por separado y agrego los resultados al final

Una de las mayores ventajas es que permite el procesamiento paralelo de los archivos (ya sea múltiples subprocesos o procesos)

La otra ventaja es que la manipulación de archivos (como agregar / eliminar fechas en el ejemplo) se puede lograr mediante comandos de shell regulares, lo que no es posible en formatos de archivo más avanzados / complicados.

Este enfoque no cubre todos los escenarios, pero es muy útil en muchos de ellos.

2
103

Ahora, dos años después de la pregunta, hay un equivalente de pandas 'fuera del núcleo': dask . ¡Es excelente! Aunque no es compatible con todas las funciones de pandas, puede llegar muy lejos con él. Actualización: en los últimos dos años se ha mantenido constantemente y hay una comunidad de usuarios sustancial que trabaja con Dask.

Y ahora, cuatro años después de la pregunta, hay otro equivalente de pandas 'fuera del núcleo' de alto rendimiento en Vaex . "Utiliza mapeo de memoria, política de copia de memoria cero y cálculos perezosos para un mejor rendimiento (sin desperdicio de memoria)". Puede manejar conjuntos de datos de miles de millones de filas y no los almacena en la memoria (lo que incluso hace posible realizar análisis en hardware subóptimo).

3
  • 6
    y para ver un ejemplo completamente elaborado con dask, solo eche un vistazo aquí stackoverflow.com/questions/37979167/… 9 de febrero de 2017 a las 15:58
  • 1
    Dependiendo de sus datos, tiene sentido echar un vistazo a pystore . Se basa en dask.
    gies0r
    1 de mayo de 2020 a las 20:54
  • 4
    ¿Está siempre "fuera de núcleo"? (es decir, ¿no requiere mucha RAM?). Si no tiene un clúster a mano, Dask no es una buena solución, en mi humilde opinión. Citando de la propia documentación de Dask : "Si está buscando administrar un terabyte o menos de datos tabulares CSV o JSON, entonces debe olvidarse de Spark y Dask y usar Postgres o MongoDB". 30 jul.20 a las 15:51
70

Si sus conjuntos de datos tienen entre 1 y 20 GB, debe obtener una estación de trabajo con 48 GB de RAM. Entonces Pandas puede contener todo el conjunto de datos en RAM. Sé que no es la respuesta que está buscando aquí, pero hacer computación científica en una computadora portátil con 4 GB de RAM no es razonable.

7
  • 11
    "hacer computación científica en una computadora portátil con 4GB de RAM no es razonable" Defina razonable. Creo que UNIVAC adoptaría un punto de vista diferente. arstechnica.com/tech-policy/2011/09/… 26/08/15 a las 14:42
  • 3
    ¡Acordado! intente seguir trabajando en la memoria incluso si cuesta $$ por adelantado. Si su trabajo genera un rendimiento financiero, con el tiempo recuperará los gastos gracias a su mayor eficiencia.
    ansonw
    10/11/2017 a las 17:57
  • 4
    Hacer informática científica en una estación de trabajo con 48 GB de RAM no es razonable. 6 de abril de 2019 a las 6:52
  • 6
    @YaroslavNikitenko Un r4.2xlarge con 61GB / RAM cuesta $ .532 / hora. ¿Qué tipo de informática científica está haciendo que no sea tan valiosa? Suena inusual, si no irrazonable.
    rjurney
    6/04/19 a las 22:39
  • 4
    @rjurney lo siento, tal vez debería haber eliminado mi comentario. Su juicio sobre la computadora científica "irrazonable" parece muy subjetivo. Hago mis cálculos científicos durante años en computadoras portátiles, y eso me parece suficiente, porque la mayoría de las veces escribo código. Mis algoritmos son mucho más difíciles desde el punto de vista de la programación que desde el computacional. También estoy bastante seguro de que para escribir algoritmos escalables uno no debería depender de las limitaciones actuales del hardware. Su comentario sobre la informática de otras personas puede sonar un poco ofensivo (aparte de la subjetividad), ¿le importaría borrar estas pocas palabras? 7 abr .19 a las 6:05
64

Sé que este es un hilo antiguo, pero creo que vale la pena echarle un vistazo a la biblioteca Blaze . Está diseñado para este tipo de situaciones.

De los documentos:

Blaze extiende la usabilidad de NumPy y Pandas a la computación distribuida y fuera del núcleo. Blaze proporciona una interfaz similar a la de NumPy ND-Array o Pandas DataFrame, pero asigna estas interfaces familiares a una variedad de otros motores computacionales como Postgres o Spark.

Editar: Por cierto, es compatible con ContinuumIO y Travis Oliphant, autor de NumPy.

1
  • Otra biblioteca que podría valer la pena ver es GraphLab Create: tiene una estructura eficiente tipo DataFrame que no está limitada por la capacidad de memoria. blog.dato.com/… 22/04/15 a las 16:17
57

Este es el caso de pymongo. También hice prototipos usando sql server, sqlite, HDF, ORM (SQLAlchemy) en python. En primer lugar, pymongo es una base de datos basada en documentos, por lo que cada persona sería un documento ( dictde atributos). Muchas personas forman una colección y puedes tener muchas colecciones (personas, bolsa, ingresos).

pd.dateframe -> pymongo Nota: uso el chunksizein read_csvpara mantenerlo en 5 a 10k registros (pymongo deja caer el socket si es más grande)

aCollection.insert((a[1].to_dict() for a in df.iterrows()))

consultando: gt = mayor que ...

pd.DataFrame(list(mongoCollection.find({'anAttribute':{'$gt':2887000, '$lt':2889000}})))

.find()devuelve un iterador, por lo que comúnmente uso ichunkedpara cortar en iteradores más pequeños.

¿Qué tal una unión, ya que normalmente obtengo 10 fuentes de datos para pegar juntas?

aJoinDF = pandas.DataFrame(list(mongoCollection.find({'anAttribute':{'$in':Att_Keys}})))

luego (en mi caso, a veces tengo que agregar aJoinDFprimero antes de que sea "fusionable").

df = pandas.merge(df, aJoinDF, on=aKey, how='left')

Y luego puede escribir la nueva información en su colección principal a través del método de actualización a continuación. (colección lógica vs fuentes de datos físicas).

collection.update({primarykey:foo},{key:change})

En búsquedas más pequeñas, simplemente desnormalice. Por ejemplo, tiene código en el documento y simplemente agrega el texto del código de campo y realiza una dictbúsqueda mientras crea documentos.

Ahora que tiene un buen conjunto de datos basado en una persona, puede dar rienda suelta a su lógica en cada caso y crear más atributos. Finalmente, puede leer en pandas sus 3 indicadores clave de memoria máxima y hacer pivotes / agg / exploración de datos. Esto me funciona para 3 millones de registros con números / texto grande / categorías / códigos / flotantes / ...

También puede usar los dos métodos integrados en MongoDB (MapReduce y marco agregado). Consulte aquí para obtener más información sobre el marco agregado , ya que parece ser más fácil que MapReduce y parece útil para un trabajo agregado rápido. Observe que no necesito definir mis campos o relaciones y puedo agregar elementos a un documento. En el estado actual del conjunto de herramientas numpy, pandas, python que cambia rápidamente, MongoDB me ayuda a ponerme a trabajar :)

2
  • Hola, estoy jugando con su ejemplo, así y me encuentro con este error al intentar insertar en una base de datos: In [96]: test.insert((a[1].to_dict() for a in df.iterrows())) --------------- InvalidDocument: Cannot encode object: 0. ¿Alguna idea de lo que podría estar mal? Mi marco de datos consta de todos los tipos de datos int64 y es muy simple. 16 de enero de 2013 a las 2:53
  • 2
    Sí, hice lo mismo para un DF de rango simple y el int64 de numpy parece molestar a pymongo. Todos los datos con los que he jugado se convierten de CSV (vs artificialmente a través de range ()) y tienen tipos largos y, por lo tanto, no hay problemas. En numpy puedes convertir, pero lo veo como una detracción. Debo admitir que los elementos 10.1 para HDF parecen emocionantes. 17/01/2013 a las 22:01
53

Un truco que encontré útil para casos de uso de datos grandes es reducir el volumen de los datos reduciendo la precisión flotante a 32 bits. No es aplicable en todos los casos, pero en muchas aplicaciones la precisión de 64 bits es excesiva y el ahorro de memoria 2x vale la pena. Para hacer un punto obvio aún más obvio:

>>> df = pd.DataFrame(np.random.randn(int(1e8), 5))
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100000000 entries, 0 to 99999999
Data columns (total 5 columns):
...
dtypes: float64(5)
memory usage: 3.7 GB

>>> df.astype(np.float32).info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100000000 entries, 0 to 99999999
Data columns (total 5 columns):
...
dtypes: float32(5)
memory usage: 1.9 GB
1
  • En términos generales, creo que debería dar una idea de cada tipo de columna y tener un dictado que los maneje de manera consistente. Esto permite una mayor reducción (a una categoría int pequeña, o incluso mejor). Dependiendo del propósito, incluso puede usar algún 'truco' como cambiar la unidad que usa (usar k $ podría ayudarlo a rebajar todo a int16) o agrupar las cosas en categorías. Idealmente, eso debería hacerse antes de la primera exportación. 24 oct.20 a las 9:08
48

Me di cuenta de esto un poco tarde, pero trabajo con un problema similar (modelos de prepago de hipotecas). Mi solución ha sido omitir la capa pandas HDFStore y usar pytables directas. Guardo cada columna como una matriz HDF5 individual en mi archivo final.

Mi flujo de trabajo básico es obtener primero un archivo CSV de la base de datos. Lo gzipé, por lo que no es tan grande. Luego, lo convierto en un archivo HDF5 orientado a filas, iterando sobre él en Python, convirtiendo cada fila en un tipo de datos real y escribiéndolo en un archivo HDF5. Eso lleva algunas decenas de minutos, pero no usa memoria, ya que solo funciona fila por fila. Luego "transpongo" el archivo HDF5 orientado a filas a un archivo HDF5 orientado a columnas.

La transposición de la tabla se ve así:

def transpose_table(h_in, table_path, h_out, group_name="data", group_path="/"):
    # Get a reference to the input data.
    tb = h_in.getNode(table_path)
    # Create the output group to hold the columns.
    grp = h_out.createGroup(group_path, group_name, filters=tables.Filters(complevel=1))
    for col_name in tb.colnames:
        logger.debug("Processing %s", col_name)
        # Get the data.
        col_data = tb.col(col_name)
        # Create the output array.
        arr = h_out.createCArray(grp,
                                 col_name,
                                 tables.Atom.from_dtype(col_data.dtype),
                                 col_data.shape)
        # Store the data.
        arr[:] = col_data
    h_out.flush()

Leerlo de nuevo se ve así:

def read_hdf5(hdf5_path, group_path="/data", columns=None):
    """Read a transposed data set from a HDF5 file."""
    if isinstance(hdf5_path, tables.file.File):
        hf = hdf5_path
    else:
        hf = tables.openFile(hdf5_path)

    grp = hf.getNode(group_path)
    if columns is None:
        data = [(child.name, child[:]) for child in grp]
    else:
        data = [(child.name, child[:]) for child in grp if child.name in columns]

    # Convert any float32 columns to float64 for processing.
    for i in range(len(data)):
        name, vec = data[i]
        if vec.dtype == np.float32:
            data[i] = (name, vec.astype(np.float64))

    if not isinstance(hdf5_path, tables.file.File):
        hf.close()
    return pd.DataFrame.from_items(data)

Ahora, generalmente ejecuto esto en una máquina con una tonelada de memoria, por lo que es posible que no sea lo suficientemente cuidadoso con el uso de mi memoria. Por ejemplo, de forma predeterminada, la operación de carga lee todo el conjunto de datos.

Esto generalmente funciona para mí, pero es un poco torpe y no puedo usar la elegante magia de Pytables.

Editar: la ventaja real de este enfoque, sobre la matriz de registros de pytables predeterminada, es que luego puedo cargar los datos en R usando h5r, que no puede manejar tablas. O, al menos, no he podido hacer que cargue tablas heterogéneas.

4
  • ¿Le importaría compartir conmigo parte de su código? Estoy interesado en cómo carga los datos de algún formato de texto plano sin conocer los tipos de datos antes de enviarlos a pytables. Además, parece que solo trabaja con datos de un tipo. ¿Es eso correcto? 27 de marzo de 2013 a las 2:01
  • 1
    En primer lugar, supongo que conozco los tipos de columnas antes de cargarlas, en lugar de intentar adivinar a partir de los datos. Guardo un archivo JSON de "especificaciones de datos" con los nombres y tipos de columna y lo uso al procesar los datos. (El archivo suele ser una salida BCP horrible sin etiquetas). Los tipos de datos que utilizo son cadenas, flotantes, enteros o fechas mensuales. Convierto las cadenas en ints guardando una tabla de enumeración y convierto las fechas en ints (meses pasados ​​2000), por lo que solo me quedan ints y flotadores en mis datos, más la enumeración. Ahora guardo los flotadores como float64, pero experimenté con float32. 28/03/13 a las 16:34
  • 1
    Si tiene tiempo, pruebe esto para una compatibilidad externa con R: pandas.pydata.org/pandas-docs/dev/… , y si tiene alguna dificultad, tal vez podamos modificarlo.
    Jeff
    1/04/2013 a las 16:50
  • Lo intentaré, si puedo. rhdf5 es una molestia, ya que es un paquete de bioconductores, en lugar de estar en CRAN como h5r. Estoy a merced de nuestro equipo de arquitectura técnica y hubo algún problema con rhdf5 la última vez que lo pedí. En cualquier caso, parece un error orientarse a filas en lugar de orientarse a columnas con una tienda OLAP, pero ahora estoy divagando. 1 de abril de 2013 a las 20:09
37

Como han señalado otros, después de algunos años ha surgido un equivalente de pandas "fuera del núcleo": dask . Aunque dask no es un reemplazo directo de los pandas y toda su funcionalidad, se destaca por varias razones:

Dask es una biblioteca de computación paralela flexible para computación analítica que está optimizada para la programación de tareas dinámicas para cargas de trabajo computacionales interactivas de colecciones de "Big Data" como matrices paralelas, marcos de datos y listas que extienden interfaces comunes como NumPy, Pandas o iteradores de Python a iteradores más grandes. que la memoria o entornos distribuidos y escala desde portátiles a clústeres.

Dask emphasizes the following virtues:

  • Familiar: Provides parallelized NumPy array and Pandas DataFrame objects
  • Flexible: Provides a task scheduling interface for more custom workloads and integration with other projects.
  • Native: Enables distributed computing in Pure Python with access to the PyData stack.
  • Fast: Operates with low overhead, low latency, and minimal serialization necessary for fast numerical algorithms
  • Scales up: Runs resiliently on clusters with 1000s of cores Scales down: Trivial to set up and run on a laptop in a single process
  • Responsive: Designed with interactive computing in mind it provides rapid feedback and diagnostics to aid humans

y para agregar una muestra de código simple:

import dask.dataframe as dd
df = dd.read_csv('2015-*-*.csv')
df.groupby(df.user_id).value.mean().compute()

reemplaza algunos códigos de pandas como este:

import pandas as pd
df = pd.read_csv('2015-01-01.csv')
df.groupby(df.user_id).value.mean()

y, especialmente, proporciona a través de la concurrent.futuresinterfaz una infraestructura general para el envío de tareas personalizadas:

from dask.distributed import Client
client = Client('scheduler:port')

futures = []
for fn in filenames:
    future = client.submit(load, fn)
    futures.append(future)

summary = client.submit(summarize, futures)
summary.result()
2
  • He agregado esta respuesta desde que la publicación de @Private aparece regularmente en la lista de eliminación sugerida por contenido y longitud.
    wp78de
    22 de noviembre de 2017 a las 3:57
  • Dask es excelente a menos que necesite una indexación múltiple. La falta de indexación múltiple en el momento de redactar este documento es un problema importante. 8 de septiembre de 2020 a las 4:41
24

Vale la pena mencionar aquí también a Ray ,
es un marco de computación distribuido, que tiene su propia implementación para pandas de forma distribuida.

Simplemente reemplace la importación de pandas y el código debería funcionar como está:

# import pandas as pd
import ray.dataframe as pd

# use pd as usual

puede leer más detalles aquí:

https://rise.cs.berkeley.edu/blog/pandas-on-ray/


Actualización: la parte que maneja la distribución de pandas, ha sido extraída al proyecto modin .

la forma correcta de usarlo ahora es:

# import pandas as pd
import modin.pandas as pd
21

Una variación más

Muchas de las operaciones realizadas en pandas también se pueden realizar como una consulta de base de datos (sql, mongo)

El uso de RDBMS o mongodb le permite realizar algunas de las agregaciones en la consulta de base de datos (que está optimizada para datos grandes y utiliza la caché y los índices de manera eficiente)

Más tarde, puede realizar el procesamiento posterior utilizando pandas.

La ventaja de este método es que obtiene las optimizaciones de la base de datos para trabajar con datos grandes, al tiempo que define la lógica en una sintaxis declarativa de alto nivel, y no tiene que lidiar con los detalles de decidir qué hacer en la memoria y qué hacer fuera. de núcleo.

Y aunque el lenguaje de consulta y los pandas son diferentes, normalmente no es complicado traducir parte de la lógica de uno a otro.

14

Considere Ruffus si sigue el camino simple de crear una canalización de datos que se divide en varios archivos más pequeños.

13

Me gustaría señalar el paquete Vaex.

Vaex is a python library for lazy Out-of-Core DataFrames (similar to Pandas), to visualize and explore big tabular datasets. It can calculate statistics such as mean, sum, count, standard deviation etc, on an N-dimensional grid up to a billion (109) objects/rows per second. Visualization is done using histograms, density plots and 3d volume rendering, allowing interactive exploration of big data. Vaex uses memory mapping, zero memory copy policy and lazy computations for best performance (no memory wasted).

Eche un vistazo a la documentación: https://vaex.readthedocs.io/en/latest/ La API está muy cerca de la API de pandas.

11

Recientemente me encontré con un problema similar. Descubrí que simplemente leer los datos en fragmentos y agregarlos a medida que los escribo en fragmentos al mismo csv funciona bien. Mi problema fue agregar una columna de fecha basada en información en otra tabla, usando el valor de ciertas columnas de la siguiente manera. Esto puede ayudar a aquellos confundidos por dask y hdf5 pero más familiarizados con pandas como yo.

def addDateColumn():
"""Adds time to the daily rainfall data. Reads the csv as chunks of 100k 
   rows at a time and outputs them, appending as needed, to a single csv. 
   Uses the column of the raster names to get the date.
"""
    df = pd.read_csv(pathlist[1]+"CHIRPS_tanz.csv", iterator=True, 
                     chunksize=100000) #read csv file as 100k chunks

    '''Do some stuff'''

    count = 1 #for indexing item in time list 
    for chunk in df: #for each 100k rows
        newtime = [] #empty list to append repeating times for different rows
        toiterate = chunk[chunk.columns[2]] #ID of raster nums to base time
        while count <= toiterate.max():
            for i in toiterate: 
                if i ==count:
                    newtime.append(newyears[count])
            count+=1
        print "Finished", str(chunknum), "chunks"
        chunk["time"] = newtime #create new column in dataframe based on time
        outname = "CHIRPS_tanz_time2.csv"
        #append each output to same csv, using no header
        chunk.to_csv(pathlist[2]+outname, mode='a', header=None, index=None)
0

El formato de archivo de parquet es ideal para el caso de uso que describió. Puede leer de manera eficiente en un subconjunto específico de columnas conpd.read_parquet(path_to_file, columns=["foo", "bar"])

https://pandas.pydata.org/docs/reference/api/pandas.read_parquet.html

-2

En este momento estoy trabajando "como" tú, solo que en una escala menor, por lo que no tengo un PoC para mi sugerencia.

Sin embargo, parece que tengo éxito al usar pickle como sistema de almacenamiento en caché y subcontratar la ejecución de varias funciones en archivos: ejecutar estos archivos desde mi comando / archivo principal; Por ejemplo, utilizo prepare_use.py para convertir tipos de objetos, dividir un conjunto de datos en un conjunto de datos de prueba, validación y predicción.

¿Cómo funciona el almacenamiento en caché con pickle? Utilizo cadenas para acceder a los archivos pickle que se crean dinámicamente, dependiendo de qué parámetros y conjuntos de datos se pasaron (con eso trato de capturar y determinar si el programa ya se ejecutó, usando .shape para el conjunto de datos, dict para pasar parámetros). Respetando estas medidas, obtengo una cadena para tratar de encontrar y leer un archivo .pickle y puedo, si lo encuentro, omitir el tiempo de procesamiento para saltar a la ejecución en la que estoy trabajando en este momento.

Sin embargo, al usar bases de datos encontré problemas similares, razón por la cual encontré alegría al usar esta solución; sin duda, hay muchas restricciones, por ejemplo, almacenar enormes conjuntos de encurtidos debido a la redundancia. La actualización de una tabla de antes a después de una transformación se puede hacer con una indexación adecuada: la validación de la información abre un libro completamente diferente (intenté consolidar los datos de alquiler rastreados y dejé de usar una base de datos después de 2 horas básicamente, ya que me hubiera gustado retroceder después cada proceso de transformación)

Espero que mis 2 centavos te ayuden de alguna manera.

Saludos.