El trabajo de "datos grandes" fluye utilizando pandas

982

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 del núcleo. Sin embargo, SAS es horrible como una pieza de software por muchas otras razones.

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

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

¿Cuáles son algunos 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

Los ejemplos del mundo real serían muy apreciados, especialmente de cualquiera que use pandas en "datos grandes".

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

  1. Importe iterativamente un archivo plano grande y guárdelo en una estructura de base de datos permanente en el 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 práctica recomendada de realizar estos pasos. Al leer enlaces sobre pandas y pytables parece que agregar una nueva columna podría ser un problema.

Editar - Respondiendo a las preguntas de Jeff específicamente:

  1. Estoy construyendo modelos de riesgo de crédito al consumo. Los tipos de datos incluyen teléfono, número de seguro social y características de la dirección; valores de propiedad; información despectiva como antecedentes penales, quiebras, etc. Los conjuntos de datos que uso todos los días tienen casi 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. Raramente 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 siempre será el caso.
  5. Es bastante raro que yo subconjunto por filas al crear una nueva columna. Sin embargo, es bastante común para mí subconjuntar en filas al crear informes o generar estadísticas descriptivas. Por ejemplo, podría querer crear una frecuencia simple para una línea de negocios 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 deseo 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 los valores de las propiedades y observaré cómo se relacionan con el incumplimiento de un préstamo. Una vez que se exploran y se crean nuevas columnas, luego 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 algunos resultados. 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 crearé nuevas columnas (variables o características en estadísticas / lenguaje de aprendizaje automático).

Zelazny7
fuente
1
¿La relación tamaño del núcleo / tamaño completo es 1%, 10%? ¿Importa? Si pudieras comprimir cols a int8, o filtrar filas ruidosas, ¿eso cambiaría tu ciclo de cálculo-pensamiento de, por ejemplo, horas a minutos? (También agregue la etiqueta datos grandes.)
denis
1
Almacenar float32 en lugar de float64, e int8 cuando sea posible, debería ser trivial (aunque no sé qué herramientas / funciones hacen float64 internamente)
denis
¿Puedes dividir tu tarea en trozos de trabajo?
Andrew Scott Evans
1
una buena solución para hacer 2.019 pandas como operaciones en datos "medio" que no caben en la memoria es dask
lunguini
Hay alternativas a python + pandas que es posible que desee considerar ver cuando recién comienza. Considere el hecho de que Python es un lenguaje de programación de propósito general (no un DSL para la mezcla y el análisis de datos) y que pandas es una biblioteca añadida además de eso. Consideraría mirar R o kdb.
Henry Henrinson el

Respuestas:

622

Rutinariamente uso 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 vuelvo a agregar.

Vale la pena leer los documentos y al final de 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 tantos detalles como pueda; y puedo ayudarte a desarrollar una estructura.

  1. Tamaño de datos, # de filas, columnas, tipos de columnas; ¿Estás agregando filas o solo columnas?
  2. Cómo serán las operaciones típicas. Por ejemplo, haga una consulta en columnas para seleccionar un grupo de filas y columnas específicas, luego realice una operación (en memoria), cree nuevas columnas, guárdelas.
    (Dar un ejemplo de juguete podría permitirnos ofrecer recomendaciones más específicas).
  3. Después de ese procesamiento, ¿qué haces? ¿Es el paso 2 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 campos diferentes, 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 haces algo, o simplemente seleccionas los campos A, B, C con todos los registros (y luego haces 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 al menos pandas0.10.1 instalados.

Lea archivos iterativos trozo por trozo y múltiples consultas de tabla .

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á con una tabla grande, pero es más eficiente hacerlo de esta manera ... Creo que podría 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'] ]))

Lectura de los archivos y creación del 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 mapa_grupo, pero probablemente esto no sea necesario).

Así es como obtienes columnas y creas 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_procesamiento:

# 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 necesita definir CUALQUIER data_columns; te permiten subseleccionar filas según la columna. Por ejemplo, algo como:

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

Pueden ser más interesantes para usted en la etapa de generación del informe final (esencialmente una columna de datos está segregada de otras columnas, lo que podría impactar un poco la eficiencia si define mucho).

También es posible que desee:

  • crea una función que toma una lista de campos, busca los grupos en groups_map, luego los selecciona y concatena los resultados para obtener el marco resultante (esto es esencialmente lo que hace select_as_multiple). De esta manera, la estructura sería bastante transparente para usted.
  • í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!

Jeff
fuente
55
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.
Zelazny7
44
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 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 usa los datos es clave para cómo debe organizarse en el disco. Enviarme un correo electrónico fuera de la lista con pseudocódigo de un ejemplo más específico.
Jeff
1
He actualizado mi pregunta para responder a sus puntos detallados. Trabajaré en un ejemplo para enviarte fuera de la lista. ¡Gracias!
Zelazny7
12
@Jeff, con Pandas en 0.17.x, ¿se han resuelto los problemas descritos anteriormente en Pandas?
ctrl-alt-delete
55
@Jeff interesado en agregar una actualización rápida en su respuesta para promover dask?
Boud
137

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, lo divido en varios archivos más pequeños (ya sea por filas o por columnas)

Ejemplo: en el caso de 30 días de datos comerciales de ~ 30 GB de tamaño, lo 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 hilos 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.

usuario1827356
fuente
39
Convenido. Con todo el alboroto, es fácil olvidar que las herramientas de línea de comandos pueden ser 235 veces más rápidas que un clúster de Hadoop
zelusp el
84

Ahora hay, dos años después de la pregunta, un pandas 'fuera de núcleo' equivalente: dask . ¡Es excelente! Aunque no admite todas las funciones de pandas, puedes llegar muy lejos con él.

Privado
fuente
66
y para un ejemplo completamente resuelto con dask, solo eche un vistazo aquí stackoverflow.com/questions/37979167/…
ℕʘʘḆḽḘ
Dependiendo de sus datos, tiene sentido echar un vistazo a pystore . Se basa en dask.
gies0r
66

Si sus conjuntos de datos tienen entre 1 y 20 GB, debería obtener una estación de trabajo con 48 GB de RAM. Entonces Pandas puede contener todo el conjunto de datos en la 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.

rjurney
fuente
77
"hacer computación científica en una computadora portátil con 4GB de RAM no es razonable" Definir razonable. Creo que UNIVAC tendría una opinión diferente. arstechnica.com/tech-policy/2011/09/…
grisaitis
2
¡Convenido! intente continuar trabajando en la memoria incluso si cuesta $$ por adelantado. Si su trabajo conduce a un retorno financiero, con el tiempo, recuperará gastos a través de su mayor eficiencia.
ansonw
2
Hacer computación científica en una estación de trabajo con 48 GB de RAM no es razonable.
Yaroslav Nikitenko
44
@YaroslavNikitenko Un r4.2xlarge con 61GB / RAM es $ .532 / hora. ¿Qué tipo de computación científica estás haciendo que no es tan valioso? Suena inusual, si no irrazonable.
rjurney
44
@rjurney lo siento, tal vez debería haber eliminado mi comentario. Su opinión sobre la computadora científica "irrazonable" parece muy subjetiva. 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 punto de vista computacional. También estoy bastante seguro de que para escribir algoritmos escalables no se debe confiar en 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 eliminar estas pocas palabras?
Yaroslav Nikitenko el
59

Sé que este es un hilo antiguo, pero creo que vale la pena echarle un vistazo a la biblioteca Blaze . Está construido 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 en una variedad de otros motores computacionales como Postgres o Spark.

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

chishaku
fuente
Otra biblioteca que vale la pena mirar es GraphLab Create: tiene una estructura eficiente similar a DataFrame que no está limitada por la capacidad de memoria. blog.dato.com/…
impermeable
52

Este es el caso de pymongo. También he realizado un prototipo utilizando 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 usted puede tener muchas colecciones (personas, bolsa de valores, ingresos).

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

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

consultas: 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?

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

entonces (en mi caso, a veces tengo que agregar aJoinDFantes 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, solo desnormalizar. Por ejemplo, tiene código en el documento y simplemente agrega el texto del código de campo y realiza una dictbúsqueda a medida que crea documentos.

Ahora que tiene un buen conjunto de datos basado en una persona, puede liberar su lógica en cada caso y hacer 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 el 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 necesitaba definir mis campos o relaciones, y puedo agregar elementos a un documento. En el estado actual del conjunto de herramientas de numpy, pandas y python que cambia rápidamente, MongoDB me ayuda a ponerme a trabajar :)

brian_the_bungler
fuente
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 int64 y es muy simple.
Zelazny7
2
Sí, hice lo mismo para un rango simple DF y el int64 de numpy parece molestar a Pymongo. Todos los datos con los que he jugado se convierten desde CSV (vs artificialmente a través de range ()) y tienen tipos largos y, por lo tanto, no hay problemas. En numpy puedes convertir pero veo que eso es perjudicial. Debo admitir que los artículos 10.1 para HDF se ven emocionantes.
brian_the_bungler
43

Lo vi un poco tarde, pero trabajo con un problema similar (modelos de prepago de hipoteca). Mi solución ha sido omitir la capa HDFStore de pandas y usar pytables rectas. 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 comprimo, así que no es tan grande. Luego lo convierto en un archivo HDF5 orientado a filas, iterando sobre él en python, convirtiendo cada fila a 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 en 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 y luego 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 mi uso de memoria. Por ejemplo, de manera 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 las tablas.

Editar: La ventaja real de este enfoque, sobre el valor predeterminado de pytables de la matriz de registros, 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.

Johann Hibschman
fuente
¿te importaría compartir conmigo parte de tu código? Estoy interesado en cómo cargar los datos de algún formato de texto plano sin conocer los tipos de datos antes de pasar a pytables. Además, parece que solo trabaja con datos de un tipo. ¿Es eso correcto?
Zelazny7
1
En primer lugar, supongo que conozco los tipos de columnas antes de cargar, en lugar de tratar de adivinar a partir de los datos. Guardo un archivo de "especificación de datos" JSON con los nombres y tipos de columna y lo uso al procesar los datos. (El archivo suele ser una salida BCP horrible sin ninguna etiqueta). Los tipos de datos que uso 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), así que solo me quedan ints y flotantes en mis datos, más la enumeración. Ahora guardo los flotadores como float64, pero experimenté con float32.
Johann Hibschman
1
si tiene tiempo, por favor pruebe esto para compatibilidad externa con R: pandas.pydata.org/pandas-docs/dev/… , y si tiene dificultades, tal vez podamos ajustarlo
Jeff
Lo intentaré, si puedo. rhdf5 es una molestia, ya que es un paquete bioconductor, en lugar de solo estar en CRAN como h5r. Estoy a merced de nuestro equipo de arquitectura técnica, y hubo un problema con rhdf5 la última vez que lo solicité. En cualquier caso, parece un error ir orientado a filas en lugar de a columnas con una tienda OLAP, pero ahora estoy divagando.
Johann Hibschman
39

Un truco que encontré útil para casos de uso de datos grandes es reducir el volumen de datos al reducir la precisión de flotación a 32 bits. No es aplicable en todos los casos, pero en muchas aplicaciones la precisión de 64 bits es exagerada y los ahorros de memoria 2x valen 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
Ytsaig
fuente
27

Como han señalado otros, después de algunos años ha surgido un equivalente de pandas 'fuera de 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 dinámica de tareas para cargas de trabajo computacionales interactivas de colecciones de "Big Data", como matrices paralelas, marcos de datos y listas que extienden interfaces comunes como iteradores NumPy, Pandas o Python a grandes. que la memoria o entornos distribuidos y escalas de computadoras portátiles a clústeres.

Dask enfatiza las siguientes virtudes:

  • Familiar: proporciona una matriz paralela NumPy y objetos Pandas DataFrame
  • Flexible: proporciona una interfaz de programación de tareas para cargas de trabajo más personalizadas e integración con otros proyectos.
  • Nativo: habilita la informática distribuida en Pure Python con acceso a la pila PyData.
  • Rápido: funciona con baja sobrecarga, baja latencia y una serialización mínima necesaria para algoritmos numéricos rápidos
  • Se amplía: se ejecuta de manera resistente en clústeres con miles de núcleos Se reduce: trivial para configurar y ejecutar en una computadora portátil en un solo proceso
  • Sensible: diseñado teniendo en cuenta la informática interactiva, proporciona retroalimentación y diagnóstico rápidos para ayudar a los humanos

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 notable, 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()
wp78de
fuente
He agregado esta respuesta ya que la publicación de @Private aparece regularmente en la lista de borrado sugerido para contenido y duración.
wp78de
17

Vale la pena mencionar aquí Ray también,
es un marco de cómputo distribuido, que tiene su propia implementación para pandas de manera 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/

lev
fuente
16

Una variante más

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

El uso de un 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 de gran tamaño, mientras 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. de núcleo.

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

Ophir Yoktan
fuente
11

Considere Ruffus si sigue el camino simple de crear una tubería de datos que se divide en múltiples archivos más pequeños.

Mono de golf
fuente
9

Recientemente me encontré con un problema similar. Descubrí que simplemente leer los datos en fragmentos y agregarlos a medida que los escribo en el mismo csv funciona bien. Mi problema fue agregar una columna de fecha basada en la 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 los 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)
timpjohns
fuente
9

Me gustaría señalar el paquete Vaex.

Vaex es una biblioteca de Python para marcos de datos flojos fuera del núcleo (similar a Pandas), para visualizar y explorar grandes conjuntos de datos tabulares. Puede calcular estadísticas como media, suma, conteo, desviación estándar, etc., en una cuadrícula N-dimensional de hasta mil millones (10 9 ) objetos / filas por segundo. La visualización se realiza mediante histogramas, gráficos de densidad y representación de volumen en 3D, lo que permite la exploración interactiva de grandes datos. Vaex utiliza mapeo de memoria, política de copia de memoria cero y cálculos lentos para un mejor rendimiento (sin pérdida de memoria).

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

Robar
fuente
0

¿Por qué pandas? ¿Has probado el estándar Python ?

El uso de la biblioteca estándar de python. Pandas está sujeto a actualizaciones frecuentes, incluso con el reciente lanzamiento de la versión estable.

Usando la biblioteca estándar de Python, su código siempre se ejecutará.

Una forma de hacerlo es tener una idea de la forma en que desea que se almacenen sus datos y qué preguntas desea resolver con respecto a los datos. Luego dibuje un esquema de cómo puede organizar sus datos (tablas de pensamiento) que lo ayudarán a consultar los datos, no necesariamente a la normalización.

Puedes hacer buen uso de:

  • lista de diccionarios para almacenar los datos en la memoria, un dict es una fila,
  • generadores para procesar los datos fila tras fila para no desbordar su RAM,
  • lista de comprensión para consultar sus datos,
  • hacer uso de Counter, DefaultDict, ...
  • almacena tus datos en tu disco duro usando cualquier solución de almacenamiento que hayas elegido, json podría ser uno de ellos.

Ram y HDD se están volviendo más y más baratos con el tiempo y Python 3 estándar está ampliamente disponible y es estable.

Pelícano
fuente
-1

En este momento estoy trabajando "como" usted, solo en una escala más baja, por lo que no tengo un PoC para mi sugerencia.

Sin embargo, parece que tengo éxito en el uso de pickle como sistema de almacenamiento en caché y la subcontratación de la ejecución de varias funciones en archivos, ejecutando estos archivos desde mi comando / archivo principal; Por ejemplo, uso 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 pepinillos? 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 pasado parámetros). Respetando estas medidas, obtengo una Cadena para tratar de encontrar y leer un archivo .pickle y puedo, si se encuentra, omitir el tiempo de procesamiento para saltar a la ejecución en la que estoy trabajando en este momento.

Al usar bases de datos me encontré con problemas similares, por lo que me gustó usar esta solución, sin embargo, hay muchas restricciones, por ejemplo, almacenar grandes conjuntos de encurtidos debido a la redundancia. La actualización de una tabla desde antes hasta 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 básicamente dejé de usar una base de datos después de 2 horas, ya que me hubiera gustado retroceder después cada proceso de transformación)

Espero que mis 2 centavos te ayuden de alguna manera.

Saludos.

TiRoX
fuente