Reducir el uso de memoria en Python es difícil, porque Python en realidad no libera memoria al sistema operativo . Si elimina objetos, la memoria estará disponible para nuevos objetos de Python, pero no free()
volverá al sistema ( consulte esta pregunta ).
Si se adhiere a matrices numéricas numéricas, esas se liberan, pero los objetos en caja no.
>>> 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
Reducir el número de marcos de datos
Python mantiene nuestra memoria en una marca de agua alta, pero podemos reducir la cantidad total de marcos de datos que creamos. Al modificar su marco de datos, prefiera inplace=True
, para que no cree copias.
Otro problema común es aferrarse a copias de marcos de datos creados previamente en 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}
Puede solucionar este problema escribiendo %reset Out
para borrar su historial. Alternativamente, puede ajustar la cantidad de historial que guarda ipython ipython --cache-size=5
(el valor predeterminado es 1000).
Reducir el tamaño del marco de datos
Siempre que sea posible, evite el uso de tipos de objetos.
>>> df.dtypes
foo float64 # 8 bytes per value
bar int64 # 8 bytes per value
baz object # at least 48 bytes per value, often more
Los valores con un tipo de objeto están encuadrados, lo que significa que la matriz numpy solo contiene un puntero y tienes un objeto Python completo en el montón para cada valor en tu marco de datos. Esto incluye cadenas.
Si bien numpy admite cadenas de tamaño fijo en matrices, pandas no lo hace ( ha causado confusión en el usuario ). Esto puede marcar una diferencia 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
Es posible que desee evitar el uso de columnas de cadena o encontrar una forma de representar los datos de cadena como números.
Si tiene un marco de datos que contiene muchos valores repetidos (NaN es muy común), entonces puede usar una estructura de datos dispersa para reducir el uso de 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
Visualización del uso de memoria
Puede ver el uso de memoria ( documentos ):
>>> 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 partir de pandas 0.17.1, también puede hacer df.info(memory_usage='deep')
para ver el uso de la memoria, incluidos los objetos.
gc
módulo y llamar,gc.collect()
pero es posible que no recupere la memoria