Der Datenrahmen von Pandas ist praktisch, aber ich bin mir nicht sicher, was die Speicherverwaltung betrifft. Ich war neugierig, wo und wie er tatsächlich platziert wurde, also habe ich ihn nachgeschlagen.
import pandas as pd
df = pd.DataFrame({'A': [1, 2], 'B': [3.0, 4.0], 'C': [5, 6]})
for block in df._data.blocks:
memory_address = block.values.__array_interface__['data'][0]
memory_hex = block.values.data.hex()
print(f"({id(block)}) {block}")
print(f"<{memory_address}> {memory_hex}")
print()
(4886642416) FloatBlock: slice(1, 2, 1), 1 x 2, dtype: float64
<140474854679968> 00000000000008400000000000001040
(4886642608) IntBlock: slice(0, 4, 2), 2 x 2, dtype: int64
<140474585659872> 0100000000000000020000000000000005000000000000000600000000000000
Die Zahl in der spitzen Klammer ist die Speicheradresse, und die Zahl danach ist die hexadezimale Darstellung des Speicherwerts. Da die Spalten A und C beide Int-Werte sind, können Sie sehen, dass sie gemeinsam im Speicher zugeordnet sind. Aha?
Der Datenrahmen verwaltet Datenblöcke über eine Klasse namens BlockManger. Die Idee dazu ist der Artikel "[Eine Roadmap für umfangreiche wissenschaftliche Datenstrukturen in Python](https://wesmckinney.com/blog/a-roadmap-for-rich-scientific-data-structures-in-python/] des Autors von Pandas. ) ”Ist leicht zu verstehen.
Wenn Sie dem Typ der Variablen folgen, der im obigen Code angezeigt wird, ist dies wie folgt.
Sie können sehen, dass der Block das ndarray von NumPy enthält.
Von nun an ist es die Welt von NumPy "2.2. Advanced NumPy - Scipy Lecture Notes Sie können die Speicheradresse mit ndarray .__ array_interface__ ['data'] [0] abrufen. Und da Sie die Speicheransicht mit
ndarray.data` erhalten können, können Sie auch den Speicherwert anzeigen.
Beachten Sie, dass beim Drucken der Speicheransicht diese als "<Speicher bei 0x11b6a3ad0>" angezeigt wird. Dies ist jedoch die Adresse der Instanz der Speicheransicht, die sich von der Adresse des Werts unterscheidet. Weitere Informationen finden Sie unter "Numpy, Python3.6 - Kann nicht verstehen, warum die Adresse unterschiedlich ist? - Stapelüberlauf. warum-Adresse-ist-anders) ”.
Lassen Sie uns einige einfache Datenrahmenoperationen durchführen und experimentieren, wie sich die Speicherzuordnung ändert.
df1 = df[0:1]
(4886726416) FloatBlock: slice(1, 2, 1), 1 x 1, dtype: float64
<140474854679968> 0000000000000840
(4886727088) IntBlock: slice(0, 4, 2), 2 x 1, dtype: int64
<140474585659872> 01000000000000000500000000000000
Das erste ist das Stück der ersten Zeile. Sie sehen, dass sich die Speicheradresse nicht geändert hat und der Referenzbereich kürzer geworden ist. Die Blockinstanz hat sich geändert.
df2 = df[1:2]
(4886798416) FloatBlock: slice(1, 2, 1), 1 x 1, dtype: float64
<140474854679976> 0000000000001040
(4886798896) IntBlock: slice(0, 4, 2), 2 x 1, dtype: int64
<140474585659880> 02000000000000000600000000000000
Dies ist der Slice in der zweiten Zeile. Da alle Speicheradressen +8 sind, können Sie sehen, dass sie sich auf denselben Speicherblock beziehen, indem Sie einfach den Zeiger verschieben.
df['D'] = [True, False]
(4886642416) FloatBlock: slice(1, 2, 1), 1 x 2, dtype: float64
<140474854679968> 00000000000008400000000000001040
(4886642608) IntBlock: slice(0, 4, 2), 2 x 2, dtype: int64
<140474585659872> 0100000000000000020000000000000005000000000000000600000000000000
(4886800144) BoolBlock: slice(3, 4, 1), 1 x 2, dtype: bool
<140474855093504> 0100
Fügen Sie eine Spalte hinzu. Bei vorhandenen Spalten ändert sich nicht nur die Speicheradresse, sondern auch der Block nicht.
df3 = df.append(df)
(4886726224) IntBlock: slice(0, 1, 1), 1 x 4, dtype: int64
<140474855531008> 0100000000000000020000000000000001000000000000000200000000000000
(4509301648) FloatBlock: slice(1, 2, 1), 1 x 4, dtype: float64
<140474585317312> 0000000000000840000000000000104000000000000008400000000000001040
(4509301840) IntBlock: slice(2, 3, 1), 1 x 4, dtype: int64
<140474585630688> 0500000000000000060000000000000005000000000000000600000000000000
(4509301552) BoolBlock: slice(3, 4, 1), 1 x 4, dtype: bool
<140474855008224> 01000100
Ich habe versucht, die Linien zu kombinieren. Das Speicherlayout hat sich drastisch geändert. Es gibt auch zwei IntBlocks. Dies führt zu einer Fragmentierung. Ich möchte, dass Sie es zum richtigen Zeitpunkt zusammenstellen.
df4 = df3._consolidate()
(4509301552) BoolBlock: slice(3, 4, 1), 1 x 4, dtype: bool
<140474855008224> 01000100
(4509301648) FloatBlock: slice(1, 2, 1), 1 x 4, dtype: float64
<140474585317312> 0000000000000840000000000000104000000000000008400000000000001040
(4886728240) IntBlock: slice(0, 4, 2), 2 x 4, dtype: int64
<140475125920528> 01000000000000000200000000000000010000000000000002000000000000000500000000000000060000000000000005000000000000000600000000000000
Als ich die private Methode "_consolidate ()" aufrief, wurden die Int-Werte zusammengefasst und an der neuen Speicheradresse abgelegt.
Recommended Posts