DataFrame grande y persistente en pandas

91

Estoy explorando cambiar a python y pandas como usuario de SAS desde hace mucho tiempo.

Sin embargo, al ejecutar algunas pruebas hoy, me sorprendió que Python se quedara sin memoria al intentar pandas.read_csv()un archivo csv de 128 MB. Tenía alrededor de 200.000 filas y 200 columnas de datos en su mayoría numéricos.

Con SAS, puedo importar un archivo csv a un conjunto de datos SAS y puede ser tan grande como mi disco duro.

¿Hay algo análogo en pandas?

Regularmente trabajo con archivos grandes y no tengo acceso a una red informática distribuida.

Zelazny7
fuente
No estoy familiarizado con los pandas, pero es posible que desee revisar la iteración del archivo. pandas.pydata.org/pandas-docs/stable/…
monkut

Respuestas:

79

En principio, no debería quedarse sin memoria, pero actualmente hay problemas de memoria con read_csvarchivos grandes causados ​​por algunos problemas internos complejos de Python (esto es vago pero se conoce desde hace mucho tiempo: http://github.com/pydata / pandas / issues / 407 ).

Por el momento no hay una solución perfecta (aquí hay una tediosa: podría transcribir el archivo fila por fila en una matriz NumPy preasignada o un archivo mapeado en memoria np.mmap), pero es una en la que estaré trabajando en un futuro próximo. Otra solución es leer el archivo en partes más pequeñas (usar iterator=True, chunksize=1000) y luego concatenarlo con pd.concat. El problema surge cuando almacena todo el archivo de texto en la memoria de una sola vez.

Wes McKinney
fuente
1
Digamos que puedo leer el archivo y agruparlos todos juntos en un DataFrame. ¿El DataFrame tiene que residir en la memoria? Con SAS, puedo trabajar con conjuntos de datos de cualquier tamaño siempre que tenga espacio en el disco duro. ¿Es lo mismo con DataFrames? Tengo la impresión de que están limitados por la RAM y no por el espacio en el disco duro. Perdón por la pregunta del novato y gracias por tu ayuda. Estoy disfrutando tu libro.
Zelazny7
3
Correcto, estás limitado por la RAM. De hecho, SAS tiene un soporte mucho mejor para el procesamiento de big data "fuera del núcleo".
Wes McKinney
5
@WesMcKinney Estas soluciones alternativas ya no deberían ser necesarias, debido al nuevo cargador csv que aterrizó en 0.10, ¿verdad?
Gabriel Grant
79

¡Wes tiene razón, por supuesto! Solo estoy interviniendo para proporcionar un código de ejemplo un poco más completo. Tuve el mismo problema con un archivo de 129 Mb, que fue resuelto por:

import pandas as pd

tp = pd.read_csv('large_dataset.csv', iterator=True, chunksize=1000)  # gives TextFileReader, which is iterable with chunks of 1000 rows.
df = pd.concat(tp, ignore_index=True)  # df is DataFrame. If errors, do `list(tp)` instead of `tp`
fickludd
fuente
6
Creo que sólo se puede hacer df = concate(tp, ignore_index=True)?
Andy Hayden
@smci Intenté esto rápidamente con los mismos datos repetidos x4 (550 Mb) o x8 (1.1Gb). Curiosamente, con o sin [x para x en tp], el x4 salió bien y el x8 falló en un MemoryError.
fickludd
3
Me sale este error mientras que la usa: AssertionError: first argument must be a list-like of pandas objects, you passed an object of type "TextFileReader". ¿Alguna idea de lo que está pasando aquí?
Prince Kumar
3
Este error se solucionará en 0.14 (próximamente disponible), github.com/pydata/pandas/pull/6941 ; solución alternativa para <0.14.0 está por hacerpd.concat(list(tp), ignore_index=True)
Jeff
1
¿Qué
pasa
41

Este es un hilo más antiguo, pero solo quería descargar mi solución alternativa aquí. Inicialmente probé el chunksizeparámetro (incluso con valores bastante pequeños como 10000), pero no ayudó mucho; todavía tenía problemas técnicos con el tamaño de la memoria (mi CSV era ~ 7.5 Gb).

En este momento, solo leo fragmentos de los archivos CSV en un enfoque de bucle for y los agrego, por ejemplo, a una base de datos SQLite paso a paso:

import pandas as pd
import sqlite3
from pandas.io import sql
import subprocess

# In and output file paths
in_csv = '../data/my_large.csv'
out_sqlite = '../data/my.sqlite'

table_name = 'my_table' # name for the SQLite database table
chunksize = 100000 # number of lines to process at each iteration

# columns that should be read from the CSV file
columns = ['molecule_id','charge','db','drugsnow','hba','hbd','loc','nrb','smiles']

# Get number of lines in the CSV file
nlines = subprocess.check_output('wc -l %s' % in_csv, shell=True)
nlines = int(nlines.split()[0]) 

# connect to database
cnx = sqlite3.connect(out_sqlite)

# Iteratively read CSV and dump lines into the SQLite table
for i in range(0, nlines, chunksize):

    df = pd.read_csv(in_csv,  
            header=None,  # no header, define column header manually later
            nrows=chunksize, # number of rows to read at each iteration
            skiprows=i)   # skip rows that were already read

    # columns to read        
    df.columns = columns

    sql.to_sql(df, 
                name=table_name, 
                con=cnx, 
                index=False, # don't use CSV file index
                index_label='molecule_id', # use a unique column from DataFrame as index
                if_exists='append') 
cnx.close()    

fuente
4
Muy útil para ver un caso de uso realista para la función de lectura fragmentada. Gracias.
Alex Kestner
5
Solo un pequeño comentario, a este viejo tema: pandas.read_csvdevuelve directamente (al menos en la versión que estoy usando actualmente) un iterador si simplemente proporciona iterator=Truey chunksize=chunksize. Por lo tanto, solo haría un forbucle sobre la pd.read_csvllamada, en lugar de volver a crear una instancia cada vez. Sin embargo, esto solo cuesta los gastos generales de la llamada, es posible que no haya un impacto significativo.
Joël
1
Hola, Joel. ¡Gracias por la nota! Los parámetros iterator=Truey chunksizeya existían en ese entonces si no recuerdo mal. Tal vez hubo un error en una versión anterior que causó la explosión de la memoria; lo intentaré de nuevo la próxima vez que lea un DataFrame grande en Pandas (ahora estoy usando principalmente Blaze para tales tareas)
6

A continuación se muestra mi flujo de trabajo.

import sqlalchemy as sa
import pandas as pd
import psycopg2

count = 0
con = sa.create_engine('postgresql://postgres:pwd@localhost:00001/r')
#con = sa.create_engine('sqlite:///XXXXX.db') SQLite
chunks = pd.read_csv('..file', chunksize=10000, encoding="ISO-8859-1",
                     sep=',', error_bad_lines=False, index_col=False, dtype='unicode')

Según el tamaño de su archivo, será mejor que optimice el tamaño del fragmento.

 for chunk in chunks:
        chunk.to_sql(name='Table', if_exists='append', con=con)
        count += 1
        print(count)

Después de tener todos los datos en la base de datos, puede consultar los que necesita de la base de datos.

BEN_YO
fuente
3

Si desea cargar archivos csv enormes, dask podría ser una buena opción. Imita la API de pandas, por lo que se siente bastante similar a los pandas

enlace a dask en github

user8108173
fuente
Gracias, desde que publiqué esto, he estado usando dask y el formato de parquet.
Zelazny7
1

Puede usar Pytable en lugar de pandas df. Está diseñado para grandes conjuntos de datos y el formato de archivo está en hdf5. Entonces, el tiempo de procesamiento es relativamente rápido.

Elm662
fuente