Wie kann man abschätzen, wie viel Speicher ein Pandas DataFrame benötigt?

124

Ich habe mich gefragt ... Wenn ich beispielsweise eine 400-MB-CSV-Datei in einen Pandas-Datenrahmen lese (mit read_csv oder read_table), gibt es eine Möglichkeit, zu schätzen, wie viel Speicher dies benötigt? Ich versuche nur, ein besseres Gefühl für Datenrahmen und Speicher zu bekommen ...

Anne
quelle
Sie können sich jederzeit den Prozess und die Speichernutzung für eine einzelne Datei ansehen. Wenn Sie Linux laufen lassen , versuchen topund dann Shift + Mmeine Speichernutzung zu sortieren.
JayQuerie.com
Ich denke, ich sollte für diese offene Pandas-Ausgabe werben .
Andy Hayden
3
Ich habe einen großen Datenrahmen mit 4 Millionen Zeilen. Ich habe festgestellt, dass die Berechnung der leeren Teilmenge Sekunden x=df.loc[[]]dauert 0.1(um Nullzeilen zu extrahieren) und außerdem Hunderte von Megabyte Speicher benötigt, genau wie der ursprüngliche Datenrahmen, wahrscheinlich aufgrund von Kopien darunter.
osa
neuer Link für den alten Beitrag des Pandas-Hauptentwicklers
Saladi

Antworten:

95

df.memory_usage() gibt zurück, wie viel jede Spalte belegt:

>>> df.memory_usage()

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

Um Indizes einzuschließen, übergeben Sie index=True.

So erhalten Sie den gesamten Speicherverbrauch:

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

Durch das Übergeben deep=Truewird außerdem ein genauerer Bericht zur Speichernutzung erstellt, der die vollständige Nutzung der enthaltenen Objekte berücksichtigt.

Dies liegt daran, dass die Speichernutzung keinen Speicher enthält, der von Elementen belegt wird, die keine Komponenten des Arrays sind, wenn deep=False(Standardfall).

Aleksey Sivokon
quelle
1
Ist die Summe aller Speichernutzungen der Spalten wirklich die Auswirkung auf die Speichernutzung? Ich kann mir vorstellen, dass es mehr Overhead gibt.
Firelynx
14
Sie wollen wirklich auchdeep=True
smci
Die Summe von df.memory_usage () entspricht nicht sys.getsizeof (df)! Es gibt viele Gemeinkosten. Wie smci erwähnt, brauchen Siedeep=True
Vagabund
11
Zu Ihrer Information, memory_usage()gibt die Speichernutzung in Bytes zurück (wie erwartet).
Engelen
2
Warum so ein großer Unterschied zwischen mit / ohne deep = True?
Nguai al
81

Hier ist ein Vergleich der verschiedenen Methoden - sys.getsizeof(df)am einfachsten.

In diesem Beispiel dfhandelt es sich um einen Datenrahmen mit 814 Zeilen, 11 Spalten (2 Zoll, 9 Objekte), der aus einem 427-KB-Shapefile gelesen wird

sys.getsizeof (df)

>>> System importieren
>>> sys.getsizeof (df)
(gibt Ergebnisse in Bytes)
462456

df.memory_usage ()

>>> df.memory_usage ()
...
(listet jede Spalte mit 8 Bytes / Zeile auf)

>>> df.memory_usage (). sum ()
71712
(ungefähr Zeilen * Spalten * 8 Bytes)

>>> df.memory_usage (deep = True)
(listet die volle Speichernutzung jeder Spalte auf)

>>> df.memory_usage (deep = True) .sum ()
(gibt Ergebnisse in Bytes)
462432

df.info ()

Druckt Datenrahmeninformationen in stdout. Technisch gesehen handelt es sich hierbei um Kibibyte (KiB), nicht um Kilobyte - wie in der Dokumentzeichenfolge angegeben: "Die Speichernutzung wird in lesbaren Einheiten angezeigt (Basis-2-Darstellung)." Das Erhalten von Bytes würde sich also mit 1024 multiplizieren, z. B. 451,6 KiB = 462.438 Bytes.

>>> df.info ()
...
Speichernutzung: 70.0+ KB

>>> df.info (memory_usage = 'deep')
...
Speichernutzung: 451,6 KB
Brian Burns
quelle
Auf welches Objekt oder Modul g bezieht sich der obige Code?
Zozo
@ Zozo woops - war ein Tippfehler - behoben
Brian Burns
2
Ich benutze df.info(memory_usage="deep"), es gibt „392,6 MB“, während sys.getsizeof(df)und df.memory_usage(index=True, deep=True).sum()beide Rückkehr etwa „411718016“ (~ 411MB). Können Sie bitte erklären, warum die 3 Ergebnisse nicht konsistent sind? danke
Catbuilts
2
@BrianBurns: Gibt df.memory_usage(deep=True).sum()fast das gleiche mit zurück df.memory_usage(index=True, deep=True).sum(). In meinem Fall nimmt das indexnicht viel Speicher. Interessanterweise fand ich das 411718016/1024/1024 = 392.6, also df.info(memory_usage="deep")kann es verwendet werden 2^10, um Byte in MB zu konvertieren , was mich verwirrt. Trotzdem danke für deine Hilfe: D.
Catbuilts
1
@Catbuilts Ah, das erklärt es! df.infogibt Mebibyte (2 ^ 10) zurück, nicht Megabyte (10 ^ 6) - wird die Antwort ändern.
Brian Burns
42

Ich dachte, ich würde mehr Daten in die Diskussion einbringen.

Ich habe eine Reihe von Tests zu diesem Thema durchgeführt.

Durch die Verwendung des Python- resourcePakets habe ich die Speichernutzung meines Prozesses erhalten.

Und indem StringIOich die CSV in einen Puffer schreibe , kann ich die Größe leicht in Bytes messen.

Ich habe zwei Experimente durchgeführt, bei denen jeweils 20 Datenrahmen mit zunehmender Größe zwischen 10.000 und 1.000.000 Zeilen erstellt wurden. Beide haben 10 Spalten.

Im ersten Experiment habe ich nur Floats in meinem Datensatz verwendet.

Auf diese Weise erhöhte sich der Speicher im Vergleich zur CSV-Datei in Abhängigkeit von der Anzahl der Zeilen. (Größe in Megabyte)

Speicher und CSV-Größe in Megabyte in Abhängigkeit von der Anzahl der Zeilen mit Float-Einträgen

Beim zweiten Experiment hatte ich den gleichen Ansatz, aber die Daten im Datensatz bestanden nur aus kurzen Zeichenfolgen.

Speicher- und CSV-Größe in Megabyte in Abhängigkeit von der Anzahl der Zeilen mit Zeichenfolgeneinträgen

Es scheint, dass das Verhältnis der Größe des CSV und der Größe des Datenrahmens sehr unterschiedlich sein kann, aber die Größe des Speichers wird immer um den Faktor 2-3 größer sein (für die Rahmengrößen in diesem Experiment).

Ich würde diese Antwort gerne mit weiteren Experimenten vervollständigen. Bitte kommentieren Sie, wenn ich etwas Besonderes ausprobieren soll.

Firelynx
quelle
Was ist deine y-Achse?
Ilya V. Schurov
1
max_rss und csv Größe auf der Festplatte in Megabyte
Firelynx
31

Sie müssen dies in umgekehrter Reihenfolge tun.

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

Technisch geht es im Speicher darum (einschließlich der Indizes)

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

Also 168 MB im Speicher mit einer 400 MB-Datei, 1 MB Zeilen mit 20 Float-Spalten

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

VIEL kompakter, wenn es als binäre HDF5-Datei geschrieben wird

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

Die Daten waren zufällig, daher hilft die Komprimierung nicht allzu viel

Jeff
quelle
Das ist sehr klug! Haben Sie eine Idee, wie Sie den Speicher messen können, mit dem Sie die Datei lesen können read_csv?
Andy Hayden
Keine Ahnung, wie man AS misst, wenn man liest; IIRC es kann bis zu 2x der endgültige Speicher sein, der benötigt wird, um die Daten zu halten (aus Wes 'Artikel), aber ich denke, er hat es auf einen konstanten + endgültigen Speicher
Jeff
Ah, ich muss noch einmal lesen, ich erinnerte mich, dass 2x eine bequeme theoretische Minute für einen bestimmten Algorithmus war, wenn es noch weniger ist, was cool ist.
Andy Hayden
Sie können iotoplike top/ verwenden, htopum die E / A- Leistung (in Echtzeit) anzusehen.
Phillip Cloud
1
nbyteswird eine grobe Unterschätzung sein, wenn Sie zB Zeichenfolgen in einem Datenrahmen haben.
osa
10

Wenn Sie die dtypes Ihres Arrays kennen, können Sie direkt die Anzahl der Bytes berechnen, die zum Speichern Ihrer Daten + einige für die Python-Objekte selbst benötigt werden. Ein nützliches Attribut von numpyArrays ist nbytes. Sie können die Anzahl der Bytes aus den Arrays in einem Pandas abrufen, DataFrameindem Sie dies tun

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

objectdtype-Arrays speichern 8 Bytes pro Objekt (Objekt-dtype-Arrays speichern einen Zeiger auf ein undurchsichtiges Element PyObject). Wenn Sie also Zeichenfolgen in Ihrer CSV haben, müssen Sie berücksichtigen, dass read_csvdiese in objectdtype-Arrays umgewandelt werden, und Ihre Berechnungen entsprechend anpassen.

BEARBEITEN:

Siehe die numpyskalare Typen Seite für weitere Details über die object dtype. Da nur eine Referenz gespeichert ist, müssen Sie auch die Größe des Objekts im Array berücksichtigen. Wie auf dieser Seite angegeben, ähneln Objektarrays Python- listObjekten.

Phillip Cloud
quelle
Danke Phillip! Zur Verdeutlichung: Für eine Zeichenfolge benötigen wir 8 Byte für einen Zeiger auf ein Zeichenfolgenobjekt sowie das eigentliche Zeichenfolgenobjekt.
Anne
1
Ja, für jeden Objekttyp benötigen Sie einen 8-Byte-Zeiger + Größe (Objekt)
Viktor Kerkez
1
Schlagen Sie df.blocks.values ​​vor () Es sieht so aus, als ob df.blocks jetzt ein Diktat ist
MRocklin
8

Ja da ist. Pandas speichert Ihre Daten in zweidimensionalen Numpy- ndarrayStrukturen, die nach dtypes gruppiert sind. ndarrayist im Grunde ein rohes C-Array von Daten mit einem kleinen Header. Sie können die Größe also abschätzen, indem Sie die Größe des multiplizierendtype enthaltenen Arrays mit den Abmessungen des Arrays .

Beispiel: Wenn Sie 1000 Zeilen mit 2 np.int32und 5 np.float64Spalten haben, verfügt Ihr DataFrame über ein 2x1000- np.int32Array und ein 5x1000-Arraynp.float64 Array.

4 Bytes * 2 * 1000 + 8 Bytes * 5 * 1000 = 48000 Bytes

Viktor Kerkez
quelle
@AndyHayden Was meinst du mit den Baukosten? Die Größe einer Instanz von DataFrame?
Phillip Cloud
Danke Victor! @Andy - Irgendeine Idee, wie hoch die Baukosten sind?
Anne
Es ist nicht enthalten, hat aber pandaseine sehr effiziente Implementierung read_tablein Cython (es ist viel besser als der Loadtxt des Numpys), daher gehe ich davon aus, dass es die Daten analysiert und direkt in der Cython speichert ndarray.
Viktor Kerkez
@PhillipCloud Sie müssen es bauen, das braucht Speicher .. Ich erinnere mich an die doppelte Größe erwähnt? ...
Andy Hayden
6

Ich glaube, dies gibt der speicherinternen Größe jedes Objekt in Python. Interna müssen auf Pandas und Numpy überprüft werden

>>> import sys
#assuming the dataframe to be df 
>>> sys.getsizeof(df) 
59542497
Zaher Abdul Azeez
quelle