Réduire l'utilisation de la mémoire en Python est difficile, car Python ne libère pas réellement de mémoire vers le système d'exploitation . Si vous supprimez des objets, alors la mémoire est disponible pour les nouveaux objets Python, mais pas free()
pour revenir au système ( voir cette question ).
Si vous vous en tenez aux tableaux numpy numériques, ceux-ci sont libérés, mais les objets encadrés ne le sont pas.
>>> 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
Réduction du nombre de Dataframes
Python garde notre mémoire à un filigrane élevé, mais nous pouvons réduire le nombre total de dataframes que nous créons. Lors de la modification de votre dataframe, préférez inplace=True
, afin de ne pas créer de copies.
Un autre piège courant consiste à conserver des copies de dataframes précédemment créées dans 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}
Vous pouvez résoudre ce problème en tapant %reset Out
pour effacer votre historique. Vous pouvez également ajuster la quantité d'historique conservée par ipythonipython --cache-size=5
(la valeur par défaut est 1000).
Réduction de la taille de Dataframe
Dans la mesure du possible, évitez d'utiliser des types d'objets.
>>> df.dtypes
foo float64 # 8 bytes per value
bar int64 # 8 bytes per value
baz object # at least 48 bytes per value, often more
Les valeurs avec un objet dtype sont encadrées, ce qui signifie que le tableau numpy contient juste un pointeur et que vous avez un objet Python complet sur le tas pour chaque valeur de votre dataframe. Cela inclut les chaînes.
Alors que numpy prend en charge les chaînes de taille fixe dans les tableaux, les pandas ne le font pas ( cela a causé une confusion chez l'utilisateur ). Cela peut faire une différence significative:
>>> 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
Vous souhaiterez peut-être éviter d'utiliser des colonnes de chaîne ou trouver un moyen de représenter les données de chaîne sous forme de nombres.
Si vous avez une trame de données qui contient de nombreuses valeurs répétées (NaN est très courant), vous pouvez utiliser une structure de données éparse pour réduire l'utilisation de la mémoire:
>>> 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
Affichage de l'utilisation de la mémoire
Vous pouvez afficher l'utilisation de la mémoire ( docs ):
>>> 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
Depuis pandas 0.17.1, vous pouvez également df.info(memory_usage='deep')
voir l'utilisation de la mémoire, y compris les objets.
gc
module et appelergc.collect()
mais il peut ne pas récupérer la mémoire