📑

View and Copy of pandas.DataFrame

2021/10/17に公開

ビッグデータ分析では全てのデータ処理の結果を逐一確認することは不可能に近い。
そのため、結果に明らかな不備が出ないような誤った操作を行ってしまった場合、それに気付くことができずに誤った結果を導いてしまう。
そのような事態を避けるため、pandas でデータ処理をする際はその操作が元データに対して行われているのか、それともそのコピーに対して行われているのかを常に意識しておくべきである。

ここでは、以下のページを参考に[1] pandas の DataFrame への参照・代入がどのように行われているかをまとめておく。

Data Manipulation in Pandas

データ操作には一般に以下の 2 つの種類がある。

  • Access : データを参照 (get) する際の処理で __getitem()__ が呼ばれる
  • Assignment : データを代入 (set) する際の処理で __setitem()__ が呼ばれる

一方、pandas でのデータ処理は [], .loc, iloc などの indexing と呼ばれる操作とそれの連結 (chaining : [][] など) によって行われる。

Pandas ではどの indexing の操作に対してもその使用される状況に応じて get, set method が自動的に使い分けられる

Problems

問題が起きるのは、__getitem()___ で呼び出されたオブジェクトに対して __setitem()__ が呼び出されたときである。
Pandas では __getitem()__ が元の DataFrame の参照を返すのか、それともコピーを返すのかが定まっていない。
従って、そのような状況になった場合、元のデータが変更されるのかそれともコピーされたデータが変更されるのかが一意に定まらず、バグの元になるのである。

Simple Chaning

典型的な例の一つが以下のような chaining 処理である。

df[df['col1']=='example']]['col2'] = 10

この場合、始めの indexing [df['col1']=='example']__getitem()__ が呼び出された後、次の indexing ['col2'] = 10 によって __setitem()__ が呼び出される。
先に述べたように __getitem()__ による戻り値は参照かコピーかが保証されていないため、 SettingWithCopyWarning が出ることになる。

元データを変更したい場合、chaining 処理を行わずに .loc を用いて元データに対して indexng 処理を行えば良い。

df.loc[df['col1']=='example', 'col2'] = 10

この場合、元データへの indexing .loc[...] = に応じて __setitem()__ が呼び出されるため、無事元データを変更することができる。

気を付けなければならないのが、代入の際はとりあえず .loc を使えばよいというわけではないことである。
先に述べたように、pandas の indexing 処理は状況に応じて自動的に決まる。
従って、.loc は必ずしも set を意味せず、例えば下のように呼び出された場合は先と同様 get と set と chaining になり動作が保証されないことを注意しておく必要がある。

df.loc[df['col1'] == 'example', ('col1', 'col2')]['col2'] = 5.0

Hidden Chaining

実用上よく起こりうるのが、意図せず chaining と同様の操作を行ってしまう場合である。

df_view = df[df['col1`]=='exapmle]
# some sort of manipulations
# ...
df_view['col2'] = 10

この場合も __getitem()__ で作成された変数に対して __setitem()__ が呼び出されるため、再び SettingWithCopyWarning が出ることになる。

新しく作成したデータ df_view のみを変更したい場合、.copy() method を用いて明確にコピーを作っておく必要がある。

df_view = df[df['col1`]=='exapmle].copy()
# ...

後にその値を変更することになるとは思わず copy() を呼び出さずにコードを書いてしまうことが往々にしてあるため、不要なバグをさけるためにも、例えばメモリを気にしなくてよい場合は常にコピーを作るようにしておくなどのルールを決めておくことが大事である。

脚注
  1. 大元の英語記事は SettingwithCopyWarning: How to Fix This Warning in Pandas – Dataquest ↩︎

Discussion