Reduzir o uso de memória em Python é difícil, porque Python não libera memória de volta para o sistema operacional . Se você excluir objetos, a memória estará disponível para novos objetos Python, mas não será free()
devolvida ao sistema ( veja esta questão ).
Se você se limitar a matrizes numpy numéricas, elas serão liberadas, mas os objetos em caixas não.
>>> 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
Reduzindo o número de Dataframes
Python mantém nossa memória com marca d'água alta, mas podemos reduzir o número total de dataframes que criamos. Ao modificar seu dataframe, prefira inplace=True
, para não criar cópias.
Outra pegadinha comum é manter cópias de dataframes criados anteriormente em 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}
Você pode corrigir isso digitando %reset Out
para limpar seu histórico. Alternativamente, você pode ajustar quanto histórico o ipython mantém ipython --cache-size=5
(o padrão é 1000).
Reduzindo o tamanho do dataframe
Sempre que possível, evite usar 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
Os valores com um objeto dtype são encaixotados, o que significa que o array numpy contém apenas um ponteiro e você tem um objeto Python completo no heap para cada valor em seu dataframe. Isso inclui strings.
Enquanto o numpy suporta strings de tamanho fixo em arrays, o pandas não ( isso causa confusão ao usuário ). Isso pode fazer uma diferença 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
Você pode evitar o uso de colunas de string ou encontrar uma maneira de representar dados de string como números.
Se você tiver um dataframe que contém muitos valores repetidos (NaN é muito comum), você pode usar uma estrutura de dados esparsa para reduzir o uso de memória:
>>> 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
Visualizando o uso da memória
Você pode ver o uso de memória ( 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 do pandas 0.17.1, você também pode fazer df.info(memory_usage='deep')
para ver o uso de memória, incluindo objetos.
gc
módulo e chamar,gc.collect()
mas ele pode não recuperar a memória