はじめに
先日、社内勉強会でpandas.DataFrameのビューとコピーの話題になって、その場でiloc
メソッドをスライスで指定して検証してたら、かなり謎な挙動をしたので、今回記事として共有しておきます。
Pandas.DataFrameのビューとコピーに関しては、どちらが返されるか完全に理解するのは現実的ではないので、細かく確認しながら実装するというふうに取り組んだ方が良いかと思います。
ちなみに、ビューとコピー自体の説明については、この記事では説明を割愛しますので、あしからず。
先に結論
- 元のDataFrameのカラムがそれぞれ違う型の場合
- 省略せずにスライスを使う
- コピーを作成し、値渡しとなる。 ← これだけ違う!
- 省略してスライスを使う
- ビューを作成し、参照渡しとなる。
- 省略せずにスライスを使う
- 元のDataFrameの全カラムが同じ型の場合
- 省略せずにスライスを使う
- ビューを作成し、参照渡しとなる。
- 省略してスライスを使う
- ビューを作成し、参照渡しとなる。
- 省略せずにスライスを使う
本日のコード
解説
L3: 検証用のDataFrameを作成
今回、このようなDataFrameを検証用に作成します。
df = pd.DataFrame({'col_1': [10, 20, 30], 'col_2': ['a', 'b', 'c']})
# col_1 col_2
# 0 10 a
# 1 20 b
# 2 30 c
ポイントは、2つのカラムがそれぞれint
型とobject
型で、違う型であることです。
L9: スライス指定したilocメソッドでDataFrameを抽出
下記の通り、スライスを省略した方と省略しなかった方で、それぞれdf_slice1
とdf_slice2
という変数に格納します。
# 片方は省略せずにスライスを使用
df_slice1 = df.iloc[:2, 0:]
# もう片方は省略してスライスを使用
df_slice2 = df.iloc[:2]
L15: 元のDataFrameを更新…すると、挙動に差が出る
下記の通り、省略せずにilocを使った方は反映されないが、省略した方は反映された。
# 元のDataFrameを更新
df.iat[0, 0] = 999
print(df_slice1)
# ilocを省略しなかった方は反映されない
# つまりコピーになっている
# col_1 col_2
# 0 10 a
# 1 20 b
print(df_slice2)
# ilocを省略した方は反映される
# つまりビューになっている
# col_1 col_2
# 0 999 a
# 1 20 b
ここまでの結論
つまり、ilocを省略せずにスライスを使った場合はコピーを作成して値渡しとなり、ilocを省略してスライスを使った場合はビューを作成して参照渡しとなる。
L32: is_viewメソッドでは、誤った結果が返されるので注意
対象のDataFrameがビューであるか否かを調べてくれる、is_view
メソッド。
今回、ilocを省略してスライスを使った方はビューを作成していたのだが、挙動は以下の通りに。
ほんと謎。
明らかにビューじゃ…?
print(df_slice2._is_view)
# False
このDataFrameのカラムが全て同じ型だった場合の補足
上記の場合、ilocを省略しても省略しなくても、基本的にビューが作成される様子。
ますます謎。
ややこしくて、頭がごちゃごちゃになりそうな方は、この記事上の方「先に結論」でまとめてますので、ご参照下さい。
さいごに
今日は、DataFrameのiloc
メソッドでスライス指定をした際に、ilocが省略形か非省略形かによって、コピーを作成するかビューを作成するか変わる件について、記事にしました。
最後まで読んで頂いてありがとうございました!
何か、ツッコミがあれば、Twitter等でメッセージ頂けると嬉しいです!