Ridurre l'utilizzo della memoria in Python è difficile, perché Python non rilascia effettivamente memoria al sistema operativo . Se elimini oggetti, la memoria è disponibile per i nuovi oggetti Python, ma non free()
torna al sistema ( vedi questa domanda ).
Se ti attieni agli array numerici numpy, questi vengono liberati, ma gli oggetti in box non lo sono.
>>> import os, psutil, numpy as np
>>> def usage():
... process = psutil.Process(os.getpid())
... return process.get_memory_info()[0] / float(2 ** 20)
...
>>> usage() # initial memory usage
27.5
>>> arr = np.arange(10 ** 8) # create a large array without boxing
>>> usage()
790.46875
>>> del arr
>>> usage()
27.52734375 # numpy just free()'d the array
>>> arr = np.arange(10 ** 8, dtype='O') # create lots of objects
>>> usage()
3135.109375
>>> del arr
>>> usage()
2372.16796875 # numpy frees the array, but python keeps the heap big
Riduzione del numero di frame di dati
Python mantiene la nostra memoria al massimo livello, ma possiamo ridurre il numero totale di frame di dati che creiamo. Quando modifichi il tuo dataframe, preferisci inplace=True
, in modo da non creare copie.
Un altro trucco comune è mantenere le copie di dataframe creati in precedenza in ipython:
In [1]: import pandas as pd
In [2]: df = pd.DataFrame({'foo': [1,2,3,4]})
In [3]: df + 1
Out[3]:
foo
0 2
1 3
2 4
3 5
In [4]: df + 2
Out[4]:
foo
0 3
1 4
2 5
3 6
In [5]: Out # Still has all our temporary DataFrame objects!
Out[5]:
{3: foo
0 2
1 3
2 4
3 5, 4: foo
0 3
1 4
2 5
3 6}
Puoi risolvere questo problema digitando %reset Out
per cancellare la cronologia. In alternativa, puoi regolare la quantità di cronologia che ipython conserva ipython --cache-size=5
(il valore predefinito è 1000).
Riduzione delle dimensioni del dataframe
Ove possibile, evitare di utilizzare dtypes oggetto.
>>> df.dtypes
foo float64 # 8 bytes per value
bar int64 # 8 bytes per value
baz object # at least 48 bytes per value, often more
I valori con un oggetto dtype sono boxed, il che significa che l'array numpy contiene solo un puntatore e hai un oggetto Python completo sull'heap per ogni valore nel tuo dataframe. Ciò include le stringhe.
Mentre numpy supporta stringhe di dimensioni fisse negli array, panda no ( è causa di confusione dell'utente ). Questo può fare una differenza significativa:
>>> import numpy as np
>>> arr = np.array(['foo', 'bar', 'baz'])
>>> arr.dtype
dtype('S3')
>>> arr.nbytes
9
>>> import sys; import pandas as pd
>>> s = pd.Series(['foo', 'bar', 'baz'])
dtype('O')
>>> sum(sys.getsizeof(x) for x in s)
120
Potresti voler evitare di usare colonne stringa o trovare un modo per rappresentare i dati stringa come numeri.
Se si dispone di un dataframe che contiene molti valori ripetuti (NaN è molto comune), è possibile utilizzare una struttura dati sparsa per ridurre l'utilizzo della memoria:
>>> df1.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 39681584 entries, 0 to 39681583
Data columns (total 1 columns):
foo float64
dtypes: float64(1)
memory usage: 605.5 MB
>>> df1.shape
(39681584, 1)
>>> df1.foo.isnull().sum() * 100. / len(df1)
20.628483479893344 # so 20% of values are NaN
>>> df1.to_sparse().info()
<class 'pandas.sparse.frame.SparseDataFrame'>
Int64Index: 39681584 entries, 0 to 39681583
Data columns (total 1 columns):
foo float64
dtypes: float64(1)
memory usage: 543.0 MB
Visualizzazione dell'utilizzo della memoria
È possibile visualizzare l'utilizzo della memoria ( documenti ):
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 39681584 entries, 0 to 39681583
Data columns (total 14 columns):
...
dtypes: datetime64[ns](1), float64(8), int64(1), object(4)
memory usage: 4.4+ GB
A partire da panda 0.17.1, puoi anche df.info(memory_usage='deep')
vedere l'utilizzo della memoria inclusi gli oggetti.
gc
modulo e chiamaregc.collect()
ma potrebbe non recuperare la memoria