Come stimare la quantità di memoria necessaria a un DataFrame di Pandas?


125

Mi chiedevo ... Se sto leggendo, ad esempio, un file csv da 400 MB in un dataframe pandas (usando read_csv o read_table), c'è un modo per indovinare di quanta memoria avrà bisogno? Sto solo cercando di avere un'idea migliore dei frame di dati e della memoria ...


Puoi sempre guardare il processo e l'utilizzo della memoria per un singolo file. Se stai usando Linux, prova tope poi Shift + Ma ordinare il mio utilizzo della memoria.
JayQuerie.com

Sento che dovrei pubblicizzare questo problema dei panda aperti .
Andy Hayden

3
Ho un grande dataframe con 4 milioni di righe. Ho scoperto che il suo sottoinsieme vuoto x=df.loc[[]]impiega 0.1pochi secondi per essere calcolato (per estrarre zero righe) e, inoltre, richiede centinaia di megabyte di memoria, proprio come il dataframe originale, probabilmente a causa di alcune copie sotto.
osa

nuovo link per il vecchio post dello sviluppatore principale dei panda
saladi

Risposte:


97

df.memory_usage() restituirà quanto occupa ciascuna colonna:

>>> df.memory_usage()

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

Per includere gli indici, passa index=True.

Quindi, per ottenere il consumo di memoria complessivo:

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

Inoltre, il passaggio deep=Trueconsentirà un rapporto sull'utilizzo della memoria più accurato, che tiene conto del pieno utilizzo degli oggetti contenuti.

Questo perché l'utilizzo della memoria non include la memoria consumata da elementi che non sono componenti dell'array if deep=False(caso predefinito).


1
la somma degli utilizzi di memoria di tutte le colonne è davvero l'impatto sull'utilizzo della memoria? Posso immaginare che ci siano più spese generali.
firelynx

14
Vuoi davvero anchedeep=True
smci

La somma di df.memory_usage () non è uguale a sys.getsizeof (df)! Ci sono molte spese generali. Come accennato da smci, hai bisogno dideep=True
vagabondo

11
Cordiali saluti, memory_usage()restituisce l'utilizzo della memoria in byte (come ci si aspetterebbe).
engelen

2
Perché una così grande differenza tra with / without deep = True?
Nguai al

83

Ecco un confronto tra i diversi metodi: sys.getsizeof(df)è il più semplice.

Per questo esempio, dfè un dataframe con 814 righe, 11 colonne (2 int, 9 oggetti) - letto da uno shapefile da 427kb

sys.getsizeof (df)

>>> import sys
>>> sys.getsizeof (df)
(fornisce risultati in byte)
462.456

df.memory_usage ()

>>> df.memory_usage ()
...
(elenca ogni colonna a 8 byte / riga)

>>> df.memory_usage (). sum ()
71712
(all'incirca righe * colonne * 8 byte)

>>> df.memory_usage (deep = True)
(elenca l'utilizzo completo della memoria di ciascuna colonna)

>>> df.memory_usage (deep = True) .sum ()
(fornisce risultati in byte)
462.432

df.info ()

Stampa le informazioni del dataframe su stdout. Tecnicamente questi sono kibibyte (KiB), non kilobyte - come dice la docstring, "L'utilizzo della memoria è mostrato in unità leggibili dall'uomo (rappresentazione in base 2)". Quindi per ottenere byte si moltiplicherebbe per 1024, ad esempio 451,6 KiB = 462,438 byte.

>>> df.info ()
...
utilizzo della memoria: 70.0+ KB

>>> df.info (memory_usage = 'deep')
...
utilizzo della memoria: 451,6 KB

A quale oggetto o modulo si g riferisce il codice sopra?
zozo

@zozo woops - era un errore di battitura - risolto
Brian Burns

2
Io lo uso df.info(memory_usage="deep"), restituisce "392,6 MB", mentre sys.getsizeof(df)e df.memory_usage(index=True, deep=True).sum()sia di ritorno di circa "411.718.016" (~ 411MB). Puoi spiegare perché i 3 risultati non sono coerenti? grazie
Catbuilts

2
@BrianBurns: df.memory_usage(deep=True).sum()restituisce quasi lo stesso con df.memory_usage(index=True, deep=True).sum(). nel mio caso, non indexrichiede molta memoria. È interessante notare che l'ho scoperto 411718016/1024/1024 = 392.6, quindi df.info(memory_usage="deep")potrebbe usare 2^10per convertire byte in MB , il che mi rende confuso. Grazie comunque per il tuo aiuto: D.
Catbuilts

1
@ Catbuilts Ah, questo lo spiega! df.inforestituisce mebibyte (2 ^ 10), non megabyte (10 ^ 6) - modificherà la risposta.
Brian Burns

43

Ho pensato di portare qualche dato in più alla discussione.

Ho eseguito una serie di test su questo problema.

Utilizzando il resourcepacchetto python ho ottenuto l'utilizzo della memoria del mio processo.

E scrivendo il csv in un StringIObuffer, potrei facilmente misurarne la dimensione in byte.

Ho eseguito due esperimenti, ciascuno creando 20 frame di dati di dimensioni crescenti tra 10.000 e 1.000.000 di linee. Entrambi hanno 10 colonne.

Nel primo esperimento ho usato solo float nel mio set di dati.

In questo modo la memoria è aumentata rispetto al file csv in funzione del numero di righe. (Dimensioni in megabyte)

Memoria e dimensione CSV in megabyte in funzione del numero di righe con voci float

Il secondo esperimento ho avuto lo stesso approccio, ma i dati nel set di dati consistevano solo di brevi stringhe.

Memoria e dimensione CSV in megabyte in funzione del numero di righe con voci di stringa

Sembra che la relazione tra la dimensione del csv e la dimensione del dataframe possa variare molto, ma la dimensione in memoria sarà sempre maggiore di un fattore 2-3 (per le dimensioni dei frame in questo esperimento)

Mi piacerebbe completare questa risposta con più esperimenti, per favore commenta se vuoi che provi qualcosa di speciale.


Qual è il tuo asse y?
Ilya V. Schurov

1
max_rss e dimensione csv su disco in megabyte
firelynx

31

Devi farlo al contrario.

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

Tecnicamente la memoria riguarda questo (che include gli indici)

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

Quindi 168 MB di memoria con un file da 400 MB, 1 milione di righe di 20 colonne float

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

MOLTO più compatto se scritto come file binario HDF5

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

I dati erano casuali, quindi la compressione non aiuta molto


Questo è molto intelligente! Qualche idea su come misurare la memoria necessaria per leggere il file utilizzando read_csv?
Andy Hayden

Non ho idea di come misurare COME leggi; IIRC può essere fino a 2x la memoria finale necessaria per contenere i dati (dall'articolo di wes), ma penso che l'abbia ridotta a una memoria costante + finale
Jeff

Ah, ho bisogno di rileggere, mi sono ricordato che 2x è un comodo minimo teorico per un certo algoritmo, se è ancora meno bello.
Andy Hayden

Puoi usare iotoplike top/ htopper guardare (in tempo reale) le prestazioni IO.
Phillip Cloud

1
nbytes sarà una grossolana sottostima se hai ad esempio stringhe in un dataframe.
osa

10

Se conosci gli dtypes del tuo array, puoi calcolare direttamente il numero di byte che ci vorrà per memorizzare i tuoi dati + alcuni per gli oggetti Python stessi. Un attributo utile degli numpyarray è nbytes. Puoi ottenere il numero di byte dagli array in un panda DataFramefacendo

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

objectGli array dtype memorizzano 8 byte per oggetto (gli array dtype di oggetti memorizzano un puntatore a un opaco PyObject), quindi se hai stringhe nel tuo CSV devi tener conto che read_csvle trasformerà in objectarray dtype e aggiusterai i tuoi calcoli di conseguenza.

MODIFICARE:

Vedi la numpypagina dei tipi scalari per maggiori dettagli su object dtype. Poiché viene memorizzato solo un riferimento, è necessario tenere in considerazione anche la dimensione dell'oggetto nell'array. Come dice quella pagina, gli array di oggetti sono in qualche modo simili agli listoggetti Python .


Grazie Phillip! Giusto per chiarire: per una stringa avremmo bisogno di 8 byte per un puntatore a un oggetto stringa, più l'effettivo oggetto stringa?
Anne

1
Sì, per qualsiasi tipo di oggetto avrai bisogno di un puntatore a 8 byte + dimensione (oggetto)
Viktor Kerkez

1
Suggerisci df.blocks.values ​​() Sembra che df.blocks sia ora una
regola

8

Si C'è. I panda memorizzeranno i tuoi dati in ndarraystrutture numpy bidimensionali raggruppandoli per dtypes. ndarrayè fondamentalmente un array C grezzo di dati con una piccola intestazione. Quindi puoi stimare la sua dimensione semplicemente moltiplicando la dimensione del dtypecontenuto con le dimensioni dell'array.

Ad esempio: se hai 1000 righe con 2 np.int32e 5 np.float64colonne, il tuo DataFrame avrà un np.int32array 2x1000 e un np.float64array 5x1000 che è:

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


@ AndyHayden Cosa intendi per il costo di costruzione? La dimensione di un'istanza di DataFrame?
Phillip Cloud

Grazie Victor! @ Anddy - Qualche idea di quanto sia grande il costo di costruzione?
Anne

Non è incluso, ma pandasha un'implementazione molto efficiente di read_tableCython (è molto meglio di loadtxt di numpy) quindi presumo che analizzi e memorizzi i dati direttamente nel file ndarray.
Viktor Kerkez

@PhillipCloud devi costruirlo, questo richiede memoria .. Mi sembra di ricordare il doppio delle dimensioni menzionate? ...
Andy Hayden

6

Questo credo che questo dia la dimensione in memoria di qualsiasi oggetto in Python. Gli interni devono essere controllati per quanto riguarda panda e numpy

>>> import sys
#assuming the dataframe to be df 
>>> sys.getsizeof(df) 
59542497
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.