Error de memoria al usar pandas read_csv

79

Estoy tratando de hacer algo bastante simple, leer un archivo csv grande en un marco de datos de pandas.

data = pandas.read_csv(filepath, header = 0, sep = DELIMITER,skiprows = 2)

El código falla con a MemoryError, o simplemente nunca termina.

El uso de memoria en el administrador de tareas se detuvo en 506 Mb y después de 5 minutos sin cambios y sin actividad de CPU en el proceso lo detuve.

Estoy usando pandas versión 0.11.0.

Soy consciente de que solía haber un problema de memoria con el analizador de archivos, pero según http://wesmckinney.com/blog/?p=543 esto debería haberse solucionado.

El archivo que estoy tratando de leer es de 366 Mb, el código de arriba funciona si corto el archivo a algo corto (25 Mb).

También ha sucedido que aparece una ventana emergente que me dice que no puede escribir en la dirección 0x1e0baf93 ...

Stacktrace:

Traceback (most recent call last):
  File "F:\QA ALM\Python\new WIM data\new WIM data\new_WIM_data.py", line 25, in
 <module>
    wimdata = pandas.read_csv(filepath, header = 0, sep = DELIMITER,skiprows = 2
)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\io\parsers.py"
, line 401, in parser_f
    return _read(filepath_or_buffer, kwds)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\io\parsers.py"
, line 216, in _read
    return parser.read()
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\io\parsers.py"
, line 643, in read
    df = DataFrame(col_dict, columns=columns, index=index)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\core\frame.py"
, line 394, in __init__
    mgr = self._init_dict(data, index, columns, dtype=dtype)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\core\frame.py"
, line 525, in _init_dict
    dtype=dtype)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\core\frame.py"
, line 5338, in _arrays_to_mgr
    return create_block_manager_from_arrays(arrays, arr_names, axes)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\core\internals
.py", line 1820, in create_block_manager_from_arrays
    blocks = form_blocks(arrays, names, axes)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\core\internals
.py", line 1872, in form_blocks
    float_blocks = _multi_blockify(float_items, items)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\core\internals
.py", line 1930, in _multi_blockify
    block_items, values = _stack_arrays(list(tup_block), ref_items, dtype)
  File "C:\Program Files\Python\Anaconda\lib\site-packages\pandas\core\internals
.py", line 1962, in _stack_arrays
    stacked = np.empty(shape, dtype=dtype)
MemoryError
Press any key to continue . . .

Un poco de antecedentes: estoy tratando de convencer a la gente de que Python puede hacer lo mismo que R. Para esto, estoy tratando de replicar un script R que

data <- read.table(paste(INPUTDIR,config[i,]$TOEXTRACT,sep=""), HASHEADER, DELIMITER,skip=2,fill=TRUE)

R no solo logra leer bien el archivo anterior, sino que incluso lee varios de estos archivos en un bucle for (y luego hace algunas cosas con los datos). Si Python tiene un problema con archivos de ese tamaño, podría estar librando una batalla perdida ...

Ana
fuente
1
Definitivamente los pandas no deberían tener problemas con csvs de ese tamaño. ¿Puede publicar este archivo en línea?
Andy Hayden
1
También puede intentar pasar nrows=something smalla read_csvpara asegurarse de que no sea del tamaño del archivo lo que causa problemas, lo que, como dijo Andy, no debería ser el caso.
TomAugspurger
1
podría ser algo relacionado con "Visual Studio, usando Anaconda y PTVS" ... tal vez intente también en Python normal
Andy Hayden
3
He encontrado lo siguiente para resolver el problema: leer el csv como fragmentos csv_chunks = pandas.read_csv(filepath, sep = DELIMITER,skiprows = 1, chunksize = 10000), luego concatenar los fragmentos df = pandas.concat(chunk for chunk in csv_chunks). Todavía estoy interesado en saber por qué leerlo de una vez no funciona, para mí esto parece un problema con el lector de csv.
Anne
11
Si alguien todavía está siguiendo esto, tengo un poco de actualización. He llegado a creer que el analizador csv está bien (y muy rápido también), pero hay un problema de memoria de algún tipo al crear marcos de datos. La razón por la que creo esto: cuando uso el chunksize=1000truco para leer el csv, y luego trato de concatenar todos los fragmentos en un gran marco de datos, es en este punto que la memoria explota, con aproximadamente 3-4 veces una huella de memoria en comparación con el tamaño del archivo original. ¿Alguien tiene una idea de por qué podría explotar el marco de datos?
Anne

Respuestas:

32

Limitación de memoria de Windows

Los errores de memoria ocurren mucho con Python cuando se usa la versión de 32 bits en Windows. Esto se debe a que los procesos de 32 bits solo obtienen 2 GB de memoria para jugar de forma predeterminada.

Trucos para reducir el uso de memoria

Si no está utilizando Python de 32 bits en Windows, pero está buscando mejorar la eficiencia de su memoria mientras lee archivos csv, hay un truco.

La función pandas.read_csv toma una opción llamada dtype. Esto le permite a los pandas saber qué tipos existen dentro de sus datos csv.

Como funciona esto

Por defecto, los pandas intentarán adivinar qué tipo de dtipos tiene su archivo csv. Esta es una operación muy pesada porque mientras está determinando el tipo d, tiene que mantener todos los datos sin procesar como objetos (cadenas) en la memoria.

Ejemplo

Digamos que su csv se ve así:

name, age, birthday
Alice, 30, 1985-01-01
Bob, 35, 1980-01-01
Charlie, 25, 1990-01-01

Por supuesto, este ejemplo no es un problema para leerlo en la memoria, pero es solo un ejemplo.

Si los pandas leyeran el archivo csv anterior sin ninguna opción dtype, la edad se almacenaría como cadenas en la memoria hasta que los pandas hayan leído suficientes líneas del archivo csv para hacer una suposición calificada.

Creo que lo predeterminado en pandas es leer 1,000,000 filas antes de adivinar el tipo d.

Solución

Al especificar dtype={'age':int}como una opción a la .read_csv()voluntad, los pandas saben que la edad debe interpretarse como un número. Esto le ahorra mucha memoria.

Problema con datos corruptos

Sin embargo, si su archivo csv estuviera dañado, así:

name, age, birthday
Alice, 30, 1985-01-01
Bob, 35, 1980-01-01
Charlie, 25, 1990-01-01
Dennis, 40+, None-Ur-Bz

Luego, especificar dtype={'age':int}romperá el .read_csv()comando, porque no se puede convertir "40+"a int. ¡Así que desinfecta tus datos con cuidado!

Aquí puede ver cómo el uso de memoria de un marco de datos de pandas es mucho mayor cuando los flotantes se mantienen como cadenas:

Inténtalo tú mismo

df = pd.DataFrame(pd.np.random.choice(['1.0', '0.6666667', '150000.1'],(100000, 10)))
resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
# 224544 (~224 MB)

df = pd.DataFrame(pd.np.random.choice([1.0, 0.6666667, 150000.1],(100000, 10)))
resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
# 79560 (~79 MB)
firelynx
fuente
Puedo ver cómo esto puede acelerar la lectura de datos, pero disminuir la memoria. Seguramente no debería necesitar almacenar más de unos pocos valores de cadena por columna para adivinar el tipo de datos. Es decir, a menos que tenga un millón de columnas, o que la read_csvfunción esté haciendo algo increíblemente extraño, me sorprendería mucho si el uso de memoria fuera notablemente mayor.
Hannes Ovrén
2
@ HannesOvrén El tipo de datos no se puede adivinar antes de haber leído una parte significativa de los datos; de lo contrario, corre el riesgo de tener que cambiarlos varias veces, lo que aumenta el costo. Creo que los pandas por defecto leen el primer millón de filas antes de adivinar. Reduje 50 veces el perfil de memoria de nuestro producto basado en pandas al agregar dtypes a cargas csv.
firelynx
1
Mmmm, pensándolo bien, creo que podría ser problemático decidir si "3" va a ser un float o int a menos que también vea un "2.5" en alguna parte. Gracias por la explicación. Yo no sabía nada de esto.
Hannes Ovrén
Esto no es verdad. Con dtype es y en memoria más caro y con el tiempo más lento. Probado 6 veces con dtype en read_csv. Los promedios son: ... memoria no dtype: 12,121,429.333333334 | memoria con dtype: 12,124,160.0 ... En el tiempo probado 13 veces, los promedios son: ... time no dtypes: 2.0494697460761437 | time with dtypes: 2.100334332539485 ... Usó: import os import psutil process = psutil.Process (os.getpid ()) print (process.memory_info (). rss) ___File de datos: 1.5 millones de tres conjuntos de datos separados, columnas 90% son tipo de objeto. * Obviamente, el flotador tiene menos tamaño que el tipo de cuerda
nikolaosmparoutis
@nikolaos_mparoutis No estoy seguro de cómo obtuvo estos resultados. Tal vez quieras escribir tu propia respuesta porque es difícil seguir el código y el comentario en tu comentario. Mi respuesta es bastante antigua, tal vez algo cambió.
firelynx
6

Tuve el mismo problema de memoria con una simple lectura de un archivo de texto delimitado por tabulaciones de aproximadamente 1 GB de tamaño (más de 5,5 millones de registros) y esto resolvió el problema de memoria:

df = pd.read_csv(myfile,sep='\t') # didn't work, memory error
df = pd.read_csv(myfile,sep='\t',low_memory=False) # worked fine and in less than 30 seconds

Spyder 3.2.3 Python 2.7.13 64 bits

alce
fuente
7
Es contrario a la low_memory=False
intuición
2

Utilizo Pandas en mi caja de Linux y enfrenté muchas fugas de memoria que solo se resolvieron después de actualizar Pandas a la última versión después de clonarlo desde github.

Tarik
fuente
1

También encontré este problema cuando estaba ejecutando en una máquina virtual, o en algún otro lugar donde la memoria es estrictamente limitada. No tiene nada que ver con pandas o numpy o csv, pero siempre sucederá si intenta usar más memoria como puede usar, ni siquiera solo en python.

La única oportunidad que tienes es lo que ya has intentado, trata de cortar lo grande en pedazos más pequeños que encajen en la memoria.

Si alguna vez se preguntó de qué se trata MapReduce, lo descubrió por sí mismo ... MapReduce intentaría distribuir los fragmentos en muchas máquinas, trataría de procesar el fragmento en una máquina una tras otra.

Lo que descubrió con la concatenación de los archivos fragmentados podría ser un problema, tal vez se necesite alguna copia en esta operación ... pero al final esto tal vez lo salve en su situación actual, pero si su csv se vuelve un poco más grande podrías correr contra esa pared de nuevo ...

También podría ser que pandas sea tan inteligente que en realidad solo cargue los fragmentos de datos individuales en la memoria si hace algo con ellos, como concatenar a un gran df.

Varias cosas que puedes probar:

  • No cargue todos los datos a la vez, divídalos en partes
  • Hasta donde yo sé, hdf5 es capaz de hacer estos fragmentos automáticamente y solo carga la parte en la que trabaja actualmente su programa
  • Mire si los tipos están bien, una cadena '0.111111' necesita más memoria que un flotante
  • ¿Qué necesita realmente? Si la dirección es una cadena, es posible que no la necesite para el análisis numérico ...
  • Una base de datos puede ayudar a acceder y cargar solo las partes que realmente necesita (por ejemplo, solo el 1% de usuarios activos)
SebastiánNeubauer
fuente
1

No hay ningún error para Pandas 0.12.0 y NumPy 1.8.0.

Me las arreglé para crear un DataFrame grande y guardarlo en un archivo csv y luego leerlo con éxito. Por favor, vea el ejemplo aquí. . El tamaño del archivo es 554 Mb (incluso funcionó para archivos de 1.1 Gb, tomó más tiempo, generar una frecuencia de uso de archivos de 1.1Gb de 30 segundos). Aunque tengo 4Gb de RAM disponibles.

Mi sugerencia es intentar actualizar Pandas. Otra cosa que podría ser útil es intentar ejecutar su script desde la línea de comandos, porque para R no está usando Visual Studio (esto ya se sugirió en los comentarios a su pregunta), por lo tanto, tiene más recursos disponibles.

Oleksandr
fuente
1

Lo intenté chunksizemientras leía un archivo CSV grande

reader = pd.read_csv(filePath,chunksize=1000000,low_memory=False,header=0)

La lectura es ahora la lista. Podemos iterar readery escribir / agregar al nuevo csv o podemos realizar cualquier operación

for chunk in reader:
    print(newChunk.columns)
    print("Chunk -> File process")
    with open(destination, 'a') as f:
        newChunk.to_csv(f, header=False,sep='\t',index=False)
        print("Chunk appended to the file")
muTheTechie
fuente
0

Agregue estos: ratings = pd.read_csv (..., low_memory = False, memory_map = True )

Mi memoria con estos dos: # 319.082.496 Sin estos dos: # 349.110.272

nikolaosmparoutis
fuente
-1

Aunque esta es una solución alternativa, no tanto como una solución, intentaría convertir ese CSV a JSON (debería ser trivial) y usar el read_jsonmétodo en su lugar: he estado escribiendo y leyendo JSON / marcos de datos considerables (cientos de MB) en Pandas esto manera sin ningún problema en absoluto.

LetMeSOThat4U
fuente