LoginSignup
2
0

More than 3 years have passed since last update.

Pandasのデータフレームの値のメモリアドレスの調査方法

Last updated at Posted at 2020-03-04

Pandasのデータフレームは便利だけどメモリ管理よく分からないよね、実際どこにどう配置されているんだっけ、という雑談があって気になったので調べてみました。

調査方法

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

山カッコ内の数値がメモリアドレス、その後の数値がメモリ値の16進数表現です。
A列とC列はどちらもInt値なのでまとめてメモリ配置されていることが分かりますね。なるほどなー?

データ構造

データフレームはBlockMangerというクラスを通して、データをブロック管理しています。このあたりの思想はPandas作者の記事『A Roadmap for Rich Scientific Data Structures in Python』が分かりやすいです。

上記のコードで表れた変数の型を追っていくと次のようになっていて、

  • df: pandas.core.frame.DataFrame
    • df._data: pandas.core.internals.managers.BlockManager
      • df._data.blocks: tuple
  • block: pandas.core.internals.blocks.Block の子クラス(FloatBlockなど)
    • block.values: numpy.ndarray
      • block.values.__array_interface__: dict
      • block.values.data: memoryview

ブロックはNumPyのndarrayを保持していることが分かります。
ということで、ここからはNumPyの世界になっていて『2.2. Advanced NumPy — Scipy lecture notes』の通り ndarray.__array_interface__['data'][0] でメモリアドレスを取得できます。そして ndarray.data でmemoryviewが取得できるので、メモリ値も覗くことができます。

なお注意点としてmemoryviewをprintすると <memory at 0x11b6a3ad0> というふうに表示されるのですが、これはmemoryviewのインスタンスのアドレスであって、値のアドレスとは異なります。詳しくは『Numpy , Python3.6 - not able to understand why address is different? - Stack Overflow』を参照してください。

実験

いくつか簡単なデータフレーム操作を行って、メモリ配置がどう変わるか実験してみます。

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

まずは先頭行のスライスです。メモリアドレスは変わらず、参照範囲が短くなっていることが分かりますね。
なおブロックのインスタンスは変わっています。

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

2行目のスライスです。メモリアドレスは全て+8されているので、ポインタがズレているだけで同じメモリブロックを参照していることが分かります。

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

列の追加です。既存の列は、メモリアドレスだけでなくブロックも変化ありませんね。

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

行を結合してみました。メモリ配置がガラッと変わってますね。あとIntBlockが二つできてしまっています。
これはフラグメンテーションを引き起こすので、適当なタイミングでまとめてほしいところです。

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

プライベートメソッド _consolidate() を呼びだすと、Int値がまとめられて新しいメモリアドレスに配置されました。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0