¿Cómo estimar cuánta memoria necesitará un DataFrame de Pandas?

125

Me he estado preguntando ... Si estoy leyendo, digamos, un archivo csv de 400 MB en un marco de datos de pandas (usando read_csv o read_table), ¿hay alguna forma de estimar cuánta memoria necesitará? Solo intento tener una mejor idea de los marcos de datos y la memoria ...

Ana
fuente
Siempre puede mirar el proceso y su uso de memoria para un solo archivo. Si está ejecutando Linux, tratar topy después Shift + Mde ordenar mi uso de la memoria.
JayQuerie.com
Siento que debería anunciar este problema de pandas abiertos .
Andy Hayden
3
Tengo un marco de datos grande con 4 millones de filas. Descubrí que su subconjunto vacío x=df.loc[[]]tarda unos 0.1segundos en calcularse (para extraer cero filas) y, además, ocupa cientos de megabytes de memoria, al igual que el marco de datos original, probablemente debido a algunas copias debajo.
Osa
nuevo enlace para la publicación anterior del desarrollador principal de pandas
saladi

Respuestas:

97

df.memory_usage() devolverá cuánto ocupa cada columna:

>>> df.memory_usage()

Row_ID            20906600
Household_ID      20906600
Vehicle           20906600
Calendar_Year     20906600
Model_Year        20906600
...

Para incluir índices, pase index=True.

Entonces, para obtener el consumo total de memoria:

>>> df.memory_usage(index=True).sum()
731731000

Además, la aprobación deep=Truepermitirá un informe de uso de memoria más preciso, que representa el uso completo de los objetos contenidos.

Esto se debe a que el uso de memoria no incluye la memoria consumida por elementos que no son componentes de la matriz if deep=False(caso predeterminado).

Aleksey Sivokon
fuente
1
¿Es la suma de todos los usos de memoria de las columnas realmente el impacto en el uso de memoria? Me imagino que habrá más gastos generales.
firelynx
14
Realmente también quieresdeep=True
smci
¡La suma de df.memory_usage () no es igual a sys.getsizeof (df)! Hay muchos gastos generales. Como mencionó smci, necesitasdeep=True
vagabond
11
FYI, memory_usage()devuelve el uso de memoria en bytes (como era de esperar).
engelen
2
¿Por qué una diferencia tan grande entre con / sin deep = True?
Nguai al
83

Aquí hay una comparación de los diferentes métodos: sys.getsizeof(df)es el más simple.

Para este ejemplo, dfes un marco de datos con 814 filas, 11 columnas (2 entradas, 9 objetos) - leído de un shapefile de 427kb

sys.getsizeof (df)

>>> importar sys
>>> sys.getsizeof (df)
(da resultados en bytes)
462456

df.memory_usage ()

>>> df.memory_usage ()
...
(enumera cada columna en 8 bytes / fila)

>>> df.memory_usage (). sum ()
71712
(aproximadamente filas * columnas * 8 bytes)

>>> df.memory_usage (deep = True)
(enumera el uso total de memoria de cada columna)

>>> df.memory_usage (deep = True) .sum ()
(da resultados en bytes)
462432

df.info ()

Imprime la información del marco de datos en la salida estándar. Técnicamente, estos son kibibytes (KiB), no kilobytes, como dice la cadena de documentos, "El uso de la memoria se muestra en unidades legibles por humanos (representación en base 2)". Entonces, para obtener bytes se multiplicaría por 1024, por ejemplo, 451.6 KiB = 462,438 bytes.

>>> df.info ()
...
uso de memoria: 70.0+ KB

>>> df.info (memory_usage = 'deep')
...
uso de memoria: 451,6 KB
Brian Burns
fuente
¿A qué objeto o módulo se g refiere el código anterior?
zozo
@zozo woops - fue un error tipográfico - corregido
Brian Burns
2
Yo uso df.info(memory_usage="deep"), devuelve "392.6 MB", mientras que sys.getsizeof(df)y df.memory_usage(index=True, deep=True).sum()ambos vuelven aproximadamente "411 718 016" (~ 411MB). ¿Puede explicar por qué los 3 resultados no son consistentes? gracias
Catbuilts
2
@BrianBurns: df.memory_usage(deep=True).sum()devuelve casi lo mismo con df.memory_usage(index=True, deep=True).sum(). en mi caso, indexno ocupa mucha memoria. Curiosamente, descubrí que 411718016/1024/1024 = 392.6, por lo que df.info(memory_usage="deep")puede usar 2^10para convertir bytes a MB , lo que me confunde. Gracias por tu ayuda de todos modos: D.
Catbuilts
1
@Catbuilts ¡Ah, eso lo explica! df.infodevuelve mebibytes (2 ^ 10), no megabytes (10 ^ 6) - modificará la respuesta.
Brian Burns
43

Pensé que aportaría más datos a la discusión.

Realicé una serie de pruebas sobre este tema.

Al usar el resourcepaquete python obtuve el uso de memoria de mi proceso.

Y escribiendo el csv en un StringIObúfer, pude medir fácilmente su tamaño en bytes.

Ejecuté dos experimentos, cada uno creando 20 marcos de datos de tamaños crecientes entre 10,000 líneas y 1,000,000 de líneas. Ambos tienen 10 columnas.

En el primer experimento, usé solo flotantes en mi conjunto de datos.

Así es como la memoria aumentó en comparación con el archivo csv en función del número de líneas. (Tamaño en megabytes)

Tamaño de memoria y CSV en megabytes en función del número de filas con entradas flotantes

En el segundo experimento tuve el mismo enfoque, pero los datos en el conjunto de datos consistían solo en cadenas cortas.

Tamaño de memoria y CSV en megabytes en función del número de filas con entradas de cadena

Parece que la relación entre el tamaño del csv y el tamaño del marco de datos puede variar bastante, pero el tamaño en la memoria siempre será mayor en un factor de 2-3 (para los tamaños de marco en este experimento)

Me encantaría completar esta respuesta con más experimentos, comenta si quieres que pruebe algo especial.

firelynx
fuente
¿Cuál es tu eje y?
Ilya V. Schurov
1
tamaño max_rss y csv en el disco en megabytes
firelynx
31

Tienes que hacer esto al revés.

In [4]: DataFrame(randn(1000000,20)).to_csv('test.csv')

In [5]: !ls -ltr test.csv
-rw-rw-r-- 1 users 399508276 Aug  6 16:55 test.csv

Técnicamente, la memoria se trata de esto (que incluye los índices)

In [16]: df.values.nbytes + df.index.nbytes + df.columns.nbytes
Out[16]: 168000160

Entonces, 168 MB de memoria con un archivo de 400 MB, 1 millón de filas de 20 columnas flotantes

DataFrame(randn(1000000,20)).to_hdf('test.h5','df')

!ls -ltr test.h5
-rw-rw-r-- 1 users 168073944 Aug  6 16:57 test.h5

MUCHO más compacto cuando se escribe como un archivo HDF5 binario

In [12]: DataFrame(randn(1000000,20)).to_hdf('test.h5','df',complevel=9,complib='blosc')

In [13]: !ls -ltr test.h5
-rw-rw-r-- 1 users 154727012 Aug  6 16:58 test.h5

Los datos eran aleatorios, por lo que la compresión no ayuda demasiado

Jeff
fuente
¡Eso es muy inteligente! ¿Alguna idea de cómo medir la memoria que necesita para leer el archivo read_csv?
Andy Hayden
No tengo idea de cómo medir lo que lee; IIRC puede ser hasta el doble de la memoria final necesaria para almacenar los datos (del artículo de Wes), pero creo que lo redujo a una memoria constante + final
Jeff
Ah, necesito volver a leer, recordé que 2x era un mínimo teórico conveniente para un cierto algoritmo, si es menos, eso es genial.
Andy Hayden
Puede usar Me iotopgusta top/ htoppara ver (en tiempo real) el rendimiento de E / S.
Phillip Cloud
1
nbytesserá una gran subestimación si tiene, por ejemplo, cadenas en un marco de datos.
osa
10

Si conoce los dtypes de su matriz, puede calcular directamente la cantidad de bytes que se necesitarán para almacenar sus datos + algunos para los objetos Python en sí. Un atributo útil de las numpymatrices es nbytes. Puede obtener el número de bytes de las matrices en un pandas DataFramehaciendo

nbytes = sum(block.values.nbytes for block in df.blocks.values())

objectLas matrices dtype almacenan 8 bytes por objeto (las matrices dtype de objeto almacenan un puntero a un opaco PyObject), por lo que si tiene cadenas en su csv, debe tener en cuenta que las read_csvconvertirá en objectmatrices dtype y ajustará sus cálculos en consecuencia.

EDITAR:

Consulte la numpypágina de tipos escalares para obtener más detalles sobre object dtype. Dado que solo se almacena una referencia, también debe tener en cuenta el tamaño del objeto en la matriz. Como dice esa página, las matrices de objetos son algo similares a los listobjetos de Python .

Phillip Cloud
fuente
¡Gracias Phillip! Solo para aclarar: para una cadena, necesitaríamos 8 bytes para un puntero a un objeto de cadena, más el objeto de cadena real.
Anne
1
Sí, para cualquier tipo de objeto necesitará un puntero de 8 bytes + tamaño (objeto)
Viktor Kerkez
1
Sugerir df.blocks.values ​​() Parece que df.blocks ahora es un
dictado
8

Sí hay. Pandas almacenará sus datos en ndarrayestructuras numéricas bidimensionales agrupándolos por tipos. ndarrayes básicamente una matriz de datos en C sin procesar con un encabezado pequeño. Por lo tanto, puede estimar su tamaño simplemente multiplicando el tamaño dtypeque contiene por las dimensiones de la matriz.

Por ejemplo: si tiene 1000 filas con 2 np.int32y 5 np.float64columnas, su DataFrame tendrá una np.int32matriz 2x1000 y una np.float64matriz 5x1000 que es:

4bytes * 2 * 1000 + 8bytes * 5 * 1000 = 48000 bytes

Viktor Kerkez
fuente
@AndyHayden ¿A qué te refieres con el costo de construcción? ¿El tamaño de una instancia de DataFrame?
Phillip Cloud
¡Gracias Víctor! @Andy - ¿Alguna idea de cuán grande es el costo de construcción?
Anne
No está incluido, pero pandastiene una implementación muy eficiente read_tableen Cython (es mucho mejor que el loadtxt de numpy), así que supongo que analiza y almacena los datos directamente en ndarray.
Viktor Kerkez
@PhillipCloud tienes que construirlo, eso requiere memoria ... ¿Me parece recordar que se menciona el doble del tamaño? ...
Andy Hayden
6

Creo que esto le da el tamaño en memoria a cualquier objeto en Python. Es necesario comprobar los componentes internos con respecto a los pandas y numpy

>>> import sys
#assuming the dataframe to be df 
>>> sys.getsizeof(df) 
59542497
Zaher Abdul Azeez
fuente