Zmniejszenie zużycia pamięci w Pythonie jest trudne, ponieważ Python w rzeczywistości nie zwalnia pamięci z powrotem do systemu operacyjnego . Jeśli usuniesz obiekty, pamięć będzie dostępna dla nowych obiektów Pythona, ale nie free()
wróci do systemu ( zobacz to pytanie ).
Jeśli trzymasz się numerycznych tablic numpy, są one uwalniane, ale obiekty w pudełkach nie.
>>> 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
Zmniejszanie liczby ramek danych
Python utrzymuje naszą pamięć na wysokim znaku wodnym, ale możemy zmniejszyć całkowitą liczbę tworzonych ramek danych. Podczas modyfikowania ramki danych preferuj inplace=True
, aby nie tworzyć kopii.
Innym częstym problemem jest zatrzymywanie kopii wcześniej utworzonych ramek danych w ipythonie:
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}
Możesz to naprawić, wpisując, %reset Out
aby wyczyścić historię. Alternatywnie możesz dostosować, ile historii przechowuje ipython ipython --cache-size=5
(domyślnie jest to 1000).
Zmniejszanie rozmiaru ramki danych
Jeśli to możliwe, unikaj używania dtypów obiektów.
>>> df.dtypes
foo float64 # 8 bytes per value
bar int64 # 8 bytes per value
baz object # at least 48 bytes per value, often more
Wartości z typem obiektu są opakowane w ramkę, co oznacza, że tablica numpy zawiera po prostu wskaźnik i masz pełny obiekt Pythona na stercie dla każdej wartości w ramce danych. Obejmuje to ciągi.
Podczas gdy numpy obsługuje ciągi o stałym rozmiarze w tablicach, pandy nie ( powoduje to zamieszanie użytkownika ). Może to mieć istotne znaczenie:
>>> 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
Możesz uniknąć używania kolumn łańcuchowych lub znaleźć sposób na przedstawienie danych w postaci ciągów jako liczb.
Jeśli masz ramkę danych, która zawiera wiele powtarzających się wartości (NaN jest bardzo powszechne), możesz użyć rzadkiej struktury danych, aby zmniejszyć zużycie pamięci:
>>> 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
Przeglądanie wykorzystania pamięci
Możesz wyświetlić użycie pamięci ( dokumenty ):
>>> 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
Począwszy od pandy 0.17.1, możesz również df.info(memory_usage='deep')
zobaczyć użycie pamięci, w tym obiekty.
gc
moduł i zadzwonićgc.collect()
, ale nie może odzyskać pamięć