Jak oszacować, ile pamięci będzie potrzebować DataFrame Pandy?

125

Zastanawiałem się ... Jeśli czytam, powiedzmy, plik csv o wielkości 400 MB w ramce danych pandy (używając read_csv lub read_table), czy istnieje sposób, aby oszacować, ile pamięci będzie to potrzebne? Próbuję tylko lepiej poznać ramki danych i pamięć ...

Anne
źródło
Zawsze możesz spojrzeć na proces i zużycie pamięci dla pojedynczego pliku. Jeśli używasz Linuksa, spróbuj top, a następnie Shift + Muporządkować moje zużycie pamięci.
JayQuerie.com,
Czuję, że powinienem zareklamować ten numer z otwartymi pandami .
Andy Hayden
3
Mam dużą ramkę danych z 4 milionami wierszy. Odkryłem, że jego pusty podzbiór x=df.loc[[]]zajmuje 0.1kilka sekund, aby zostać obliczony (aby wyodrębnić zero wierszy), a ponadto zajmuje setki megabajtów pamięci, tak jak oryginalna ramka danych, prawdopodobnie z powodu kopiowania pod spodem.
osa
nowy link do starego postu głównego dewelopera pand
saladi

Odpowiedzi:

97

df.memory_usage() zwróci, ile zajmuje każda kolumna:

>>> df.memory_usage()

Row_ID            20906600
Household_ID      20906600
Vehicle           20906600
Calendar_Year     20906600
Model_Year        20906600
...

Aby dołączyć indeksy, przekaż index=True.

Aby uzyskać ogólne zużycie pamięci:

>>> df.memory_usage(index=True).sum()
731731000

Ponadto przekazanie deep=Trueumożliwi dokładniejszy raport o wykorzystaniu pamięci, który uwzględnia pełne wykorzystanie zawartych obiektów.

Dzieje się tak, ponieważ użycie pamięci nie obejmuje pamięci używanej przez elementy, które nie są składnikami tablicy if deep=False(przypadek domyślny).

Aleksey Sivokon
źródło
1
czy suma wykorzystania pamięci wszystkich kolumn naprawdę wpływa na użycie pamięci? Mogę sobie wyobrazić więcej narzutów.
firelynx
14
Naprawdę też chceszdeep=True
smci
Suma df.memory_usage () nie jest równa sys.getsizeof (df)! Istnieje wiele kosztów ogólnych. Jak wspomniał smci, potrzebujeszdeep=True
włóczęga
11
FYI, memory_usage()zwraca użycie pamięci w bajtach (zgodnie z oczekiwaniami).
engelen
2
Skąd taka ogromna różnica między with / without deep = True?
Nguai al
83

Oto porównanie różnych metod - sys.getsizeof(df)jest najprostsze.

W tym przykładzie dfjest to ramka danych z 814 wierszami, 11 kolumnami (2 int, 9 obiektów) - odczytana z pliku kształtu o rozmiarze 427 kb

sys.getsizeof (df)

>>> import sys
>>> sys.getsizeof (df)
(daje wyniki w bajtach)
462456

df.memory_usage ()

>>> df.memory_usage ()
...
(wyświetla każdą kolumnę z 8 bajtami / wiersz)

>>> df.memory_usage (). sum ()
71712
(z grubsza wiersze * cols * 8 bajtów)

>>> df.memory_usage (deep = True)
(wyświetla pełne wykorzystanie pamięci dla każdej kolumny)

>>> df.memory_usage (deep = True) .sum ()
(daje wyniki w bajtach)
462432

df.info ()

Wyświetla informacje o ramce danych na standardowe wyjście. Z technicznego punktu widzenia są to kibibajty (KiB), a nie kilobajty - jak głosi dokument: „Wykorzystanie pamięci jest pokazane w czytelnych dla człowieka jednostkach (reprezentacja podstawowa-2)”. Zatem aby uzyskać bajty, należy pomnożyć przez 1024, np. 451,6 KiB = 462,438 bajtów.

>>> df.info ()
...
użycie pamięci: 70,0+ KB

>>> df.info (memory_usage = 'deep')
...
użycie pamięci: 451,6 KB
Brian Burns
źródło
Do jakiego obiektu lub modułu g odnosi się powyższy kod?
zozo
@zozo woops - było literówką - naprawiono
Brian Burns
2
Używam df.info(memory_usage="deep"), zwraca "392,6 MB", podczas gdy sys.getsizeof(df)i df.memory_usage(index=True, deep=True).sum()zarówno zwrot w przybliżeniu "411718016" (~ 411MB). Czy możesz wyjaśnić, dlaczego te 3 wyniki nie są spójne? dzięki
Catbuilts
2
@BrianBurns: df.memory_usage(deep=True).sum()zwraca prawie to samo z df.memory_usage(index=True, deep=True).sum(). w moim przypadku indexnie zajmuje dużo pamięci. Co ciekawe, znalazłem to 411718016/1024/1024 = 392.6, więc mogę df.info(memory_usage="deep")użyć 2^10do konwersji bajtu na MB , co mnie dezorientuje. Mimo wszystko dzięki za pomoc: D.
Catbuilts
1
@Catbuilts Ach, to wszystko wyjaśnia! df.infozwraca mebibajty (2 ^ 10), a nie megabajty (10 ^ 6) - poprawi odpowiedź.
Brian Burns
43

Pomyślałem, że przyniosę więcej danych do dyskusji.

Przeprowadziłem serię testów w tej sprawie.

Używając resourcepakietu python , uzyskałem wykorzystanie pamięci przez mój proces.

Zapisując csv w StringIObuforze, mogłem łatwo zmierzyć jego rozmiar w bajtach.

Przeprowadziłem dwa eksperymenty, z których każdy utworzył 20 ramek danych o rosnących rozmiarach od 10 000 do 1 000 000 wierszy. Obie mają 10 kolumn.

W pierwszym eksperymencie użyłem tylko liczb zmiennoprzecinkowych w moim zbiorze danych.

W ten sposób pamięć wzrosła w porównaniu z plikiem csv jako funkcja liczby wierszy. (Rozmiar w megabajtach)

Rozmiar pamięci i pliku CSV w megabajtach jako funkcja liczby wierszy z pozycjami zmiennoprzecinkowymi

W drugim eksperymencie miałem takie samo podejście, ale dane w zbiorze danych składały się tylko z krótkich ciągów.

Rozmiar pamięci i pliku CSV w megabajtach jako funkcja liczby wierszy z wpisami w postaci ciągów

Wygląda na to, że zależność między wielkością csv a rozmiarem ramki danych może się znacznie różnić, ale rozmiar w pamięci zawsze będzie 2-3 razy większy (dla rozmiarów ramek w tym eksperymencie)

Chciałbym uzupełnić tę odpowiedź o więcej eksperymentów, proszę o komentarz, jeśli chcesz, żebym spróbował czegoś specjalnego.

firelynx
źródło
Jaka jest Twoja oś Y?
Ilya V. Schurov
1
max_rss i rozmiar csv na dysku w megabajtach
firelynx,
31

Musisz to zrobić w odwrotnej kolejności.

In [4]: DataFrame(randn(1000000,20)).to_csv('test.csv')

In [5]: !ls -ltr test.csv
-rw-rw-r-- 1 users 399508276 Aug  6 16:55 test.csv

Technicznie chodzi o pamięć (która obejmuje indeksy)

In [16]: df.values.nbytes + df.index.nbytes + df.columns.nbytes
Out[16]: 168000160

Więc 168 MB pamięci z plikiem 400 MB, 1 mln wierszy po 20 kolumn typu float

DataFrame(randn(1000000,20)).to_hdf('test.h5','df')

!ls -ltr test.h5
-rw-rw-r-- 1 users 168073944 Aug  6 16:57 test.h5

ZNACZNIE bardziej kompaktowy, gdy jest zapisywany jako binarny plik HDF5

In [12]: DataFrame(randn(1000000,20)).to_hdf('test.h5','df',complevel=9,complib='blosc')

In [13]: !ls -ltr test.h5
-rw-rw-r-- 1 users 154727012 Aug  6 16:58 test.h5

Dane były losowe, więc kompresja nie pomaga zbytnio

Jeff
źródło
To bardzo sprytne! Masz pomysł, jak zmierzyć pamięć potrzebną do odczytania pliku read_csv?
Andy Hayden
Nie mam pojęcia, jak mierzyć, jak czytasz; IIRC może być do 2x końcowej pamięci potrzebnej do przechowywania danych (z artykułu Wesa), ale myślę, że sprowadził to do stałej + końcowej pamięci
Jeff
Ach, muszę ponownie przeczytać, przypomniałem sobie, że 2x to wygodna teoretyczna minuta dla pewnego algorytmu, jeśli jest jeszcze mniej, to coool.
Andy Hayden
Możesz użyć iotoplike top/ htopdo oglądania (w czasie rzeczywistym) wydajności IO.
Phillip Cloud
1
nbytesbędzie rażącym niedoszacowaniem, jeśli masz np. ciągi znaków w ramce danych.
osa
10

Jeśli znasz dtypes swojej tablicy, możesz bezpośrednio obliczyć liczbę bajtów potrzebnych do przechowywania danych + trochę dla samych obiektów Pythona. Przydatnym atrybutem numpytablic jest nbytes. Możesz uzyskać liczbę bajtów z tablic w pandach DataFrame, wykonując

nbytes = sum(block.values.nbytes for block in df.blocks.values())

objectTablice dtype przechowują 8 bajtów na obiekt (obiekt tablice dtype przechowują wskaźnik do nieprzezroczystego PyObject), więc jeśli masz ciągi w swoim csv, musisz wziąć pod uwagę, że read_csvprzekształcą je w objecttablice dtype i odpowiednio dostosujesz obliczenia.

EDYTOWAĆ:

Zobacz numpystronę typów skalarnych, aby uzyskać więcej informacji na temat object dtype. Ponieważ przechowywane są tylko referencje, należy również wziąć pod uwagę rozmiar obiektu w tablicy. Jak mówi ta strona, tablice obiektów są nieco podobne do listobiektów Pythona .

Phillip Cloud
źródło
Dzięki, Phillip! Dla wyjaśnienia - do łańcucha potrzebowalibyśmy 8 bajtów na wskaźnik do obiektu typu string, plus rzeczywisty obiekt typu string?
Anne,
1
Tak, dla każdego typu obiektu będziesz potrzebować 8-bajtowego wskaźnika + rozmiar (obiekt)
Viktor Kerkez
1
Zaproponuj df.blocks.values () To wygląda df.blocks jest teraz DICT
MRocklin
8

Tak jest. Pandy będą przechowywać twoje dane w dwuwymiarowych ndarraystrukturach numpy , grupując je według typów. ndarrayjest w zasadzie surową tablicą danych w C z małym nagłówkiem. Możesz więc oszacować jego rozmiar, mnożąc rozmiar tego elementu dtypeprzez wymiary tablicy.

Na przykład: jeśli masz 1000 wierszy z 2 np.int32i 5 np.float64kolumnami, twoja DataFrame będzie miała jedną np.int32tablicę 2x1000 i jedną np.float64tablicę 5x1000, czyli:

4 bajty * 2 * 1000 + 8 bajtów * 5 * 1000 = 48000 bajtów

Viktor Kerkez
źródło
@AndyHayden Co masz na myśli koszt budowy? Rozmiar wystąpienia DataFrame?
Phillip Cloud
Dzięki Victor! @Andy - Masz pojęcie, jak duży jest koszt budowy?
Anne,
Nie obejmuje, ale pandasma bardzo wydajną implementację read_tablew Cythonie (jest znacznie lepsza niż loadtxt numpy), więc zakładam, że analizuje i przechowuje dane bezpośrednio w ndarray.
Viktor Kerkez
@PhillipCloud musisz to zbudować, to zajmuje pamięć ... Wydaje mi się, że pamiętam, że wspomniano dwa razy większy rozmiar? ...
Andy Hayden
6

Myślę, że to daje rozmiar w pamięci dowolnego obiektu w Pythonie. Wewnętrzne należy sprawdzić pod kątem pand i drętwienia

>>> import sys
#assuming the dataframe to be df 
>>> sys.getsizeof(df) 
59542497
Zaher Abdul Azeez
źródło