Abrir un archivo de 20GB para análisis con pandas

33

Actualmente estoy tratando de abrir un archivo con pandas y python para fines de aprendizaje automático, sería ideal para mí tenerlos a todos en un DataFrame. Ahora el archivo es de 18 GB y mi RAM es de 32 GB, pero sigo recibiendo errores de memoria.

Desde su experiencia, ¿es posible? Si no, ¿sabes de una mejor manera de evitar esto? (¿tabla de colmena? aumentar el tamaño de mi RAM a 64? crear una base de datos y acceder a ella desde python)

Hari Prasad
fuente
He tenido el mismo problema, le sugiero que aumente el intercambio, la paginación y el tamaño de su disco duro.
Medios de comunicación
La regla general cuando se cargan datos pandases que debe tener de 5 a 10 veces más RAM. Recomiendo hacer inplaceoperaciones, llamar explícitamente garbage.collectorpara desasignar objetos.
Kiritee Gak
44
Mejore esta pregunta estableciendo su objetivo final. ¿Está haciendo análisis exploratorios de datos, limpieza de datos, capacitación de un modelo o qué? ¿Qué tipo de datos?
Pete
1
¿Has considerado usar dask ?
rpanai

Respuestas:

32

Si es un archivo csv y no necesita acceder a todos los datos a la vez al entrenar su algoritmo, puede leerlo en fragmentos. El pandas.read_csvmétodo le permite leer un archivo en fragmentos como este:

import pandas as pd
for chunk in pd.read_csv(<filepath>, chunksize=<your_chunksize_here>)
    do_processing()
    train_algorithm()

Aquí está la documentación del método.

Olel Daniel
fuente
¿esto se aplica también al archivo zip?
James Wierzba
Debería funcionar si el archivo comprimido también es un archivo csv, tendría que pasar el tipo de compresión como argumento al método
Olel Daniel
22

Hay dos posibilidades: o necesita tener todos sus datos en la memoria para el procesamiento (por ejemplo, su algoritmo de aprendizaje automático querría consumirlos todos a la vez), o puede prescindir de ellos (por ejemplo, su algoritmo solo necesita muestras de filas o columnas a la vez).

En el primer caso, deberá resolver un problema de memoria . Aumente el tamaño de su memoria, alquile una máquina en la nube de alta memoria, use operaciones in situ, brinde información sobre el tipo de datos que está leyendo, asegúrese de eliminar todas las variables no utilizadas y recolectar basura, etc.

Es muy probable que 32 GB de RAM no sean suficientes para que Pandas maneje sus datos. Tenga en cuenta que el número entero "1" es solo un byte cuando se almacena como texto, pero 8 bytes cuando se representa como int64(que es el valor predeterminado cuando Pandas lo lee desde el texto). Puede hacer el mismo ejemplo con un número de coma flotante "1.0" que se expande de una cadena de 3 bytes a 8 bytes float64de forma predeterminada. Puede ganar algo de espacio al hacer que Pandas sepa con precisión qué tipos usar para cada columna y forzar las representaciones más pequeñas posibles, pero ni siquiera comenzamos a hablar de la estructura de datos de Python aquí, lo que puede agregar un puntero adicional o dos aquí o allá fácilmente , y los punteros son de 8 bytes cada uno en una máquina de 64 bits.

Para resumir: no, 32 GB de RAM probablemente no sea suficiente para que Pandas maneje un archivo de 20 GB.

En el segundo caso (que es más realista y probablemente se aplica a usted), debe resolver un problema de gestión de datos . De hecho, tener que cargar todos los datos cuando realmente solo necesita partes de ellos para el procesamiento, puede ser un signo de mala gestión de datos. Hay varias opciones aquí:

  1. Use una base de datos SQL. Si puede, es casi siempre la primera opción y una solución decentemente cómoda. 20 GB suena como el tamaño que la mayoría de las bases de datos SQL manejarían bien sin la necesidad de distribuirse incluso en una computadora portátil (de gama alta). Podrá indexar columnas, hacer agregaciones básicas a través de SQL y obtener las submuestras necesarias en Pandas para un procesamiento más complejo utilizando un simple pd.read_sql. Mover los datos a una base de datos también le brindará la oportunidad de pensar en los tipos y tamaños de datos reales de sus columnas.

  2. Si sus datos son en su mayoría numéricos (es decir, matrices o tensores), puede considerar guardarlos en un formato HDF5 (consulte PyTables ), lo que le permite leer convenientemente solo las porciones necesarias de matrices enormes del disco. Básico numpy.save y numpy.load lograr el mismo efecto mediante mapeo de memoria en las matrices en el disco también. Para los SIG y los datos ráster relacionados, existen bases de datos dedicadas , que podrían no conectarse a los pandas tan directamente como SQL, pero también deberían permitirle realizar cortes y consultas de manera razonablemente conveniente.

  3. Pandas no admite ese mapeo de memoria "parcial" de HDF5 o matrices numpy, que yo sepa. Si aún desea un tipo de solución de "pandas puros", puede intentar "fragmentar": almacenando las columnas de su tabla enorme por separado (por ejemplo, en archivos separados o en "tablas" separadas de un solo HDF5 archivo) y solo carga los necesarios a pedido, o almacena los trozos de filas por separado. Sin embargo, necesitaría implementar la lógica para cargar los fragmentos necesarios, reinventando las bicicletas ya implementadas en la mayoría de las bases de datos SQL, por lo que quizás la opción 1 aún sería más fácil aquí. Sin embargo, si sus datos vienen en un CSV, puede procesarlos en fragmentos especificando el chunksizeparámetro a pd.read_csv.

KT.
fuente
55
Algo que debería mencionarse en "el primer caso" es que si el OP tiene muchas entradas con el mismo valor en los datos (como ceros), se dice que los datos son escasos y una matriz escasa dispersa podría usarse en lugar de un marco de datos de pandas: los datos escasos requieren mucha menos memoria.
Ricardo Cruz
9

¡Acabo de tener este problema hace unos días! No estoy seguro de si esto ayuda en su caso específico, ya que no proporciona tantos detalles, pero mi situación era trabajar sin conexión en un conjunto de datos 'grande'. Los datos se obtuvieron como archivos CSV comprimidos de 20 GB de medidores de energía, datos de series de tiempo a intervalos de varios segundos.

Archivo IO:

data_root = r"/media/usr/USB STICK"
fname = r"meters001-050-timestamps.csv.gz"
this_file = os.path.join(data_root,fname)
assert os.path.exists(this_file), this_file
this_file

Cree un iterador de fragmentos directamente sobre el archivo gzip (¡no lo descomprima!)

cols_to_keep = [0,1,2,3,7]
column_names = ['METERID','TSTAMP','ENERGY','POWER_ALL','ENERGY_OUT',]
parse_dates = ['TSTAMP']
dtype={'METERID': np.int32, 
       'ENERGY': np.int32,
       'POWER_ALL': np.int32,
       'ENERGY_OUT': np.int32,
      }
df_iterator = pd.read_csv(this_file, 
                        skiprows=0, 
                        compression='gzip',
                        chunksize=1000000, 
                        usecols=cols_to_keep,
                        delimiter=";",
                        header=None,
                        names = column_names,
                      dtype=dtype,
                     parse_dates=parse_dates,
                     index_col=1,
                     )

Iterar sobre los trozos

new_df = pd.DataFrame()
count = 0
for df in df_iterator:
    chunk_df_15min = df.resample('15T').first()
    #chunk_df_30min = df.resample('30T').first()
    #chunk_df_hourly = df.resample('H').first()
    this_df = chunk_df_15min
    this_df = this_df.pipe(lambda x: x[x.METERID == 1])
    #print("chunk",i)
    new_df = pd.concat([new_df,chunk_df_15min])
    print("chunk",count, len(chunk_df_15min), 'rows added')
    #print("chunk",i, len(temp_df),'rows added')
    #break
    count += 1

Dentro del bucle de fragmentos, estoy filtrando y volviendo a muestrear a tiempo. Al hacer esto, reduje el tamaño de 20 GB a unos pocos cientos de MB HDF5 para una mayor exploración de datos fuera de línea.

Marcus Jones
fuente
5

En mi experiencia, la inicialización read_csv()con parámetros low_memory=Falsetiende a ayudar cuando se lee en archivos grandes. No creo que haya mencionado el tipo de archivo en el que está leyendo, por lo que no estoy seguro de qué tan aplicable es a su situación.

cadenaD
fuente
1

Si su archivo es un CSV, simplemente puede hacerlo en Chunk by Chunk. Simplemente puedes hacer:

import pandas as pd
for chunk in pd.read_csv(FileName, chunksize=ChunkSizeHere)
(Do your processing and training here)
Abdul
fuente