覚え書きブログ

Pythonの覚え書き(DataFrameのSettingWithCopyWarningの対処方法)

DataFrameの要素の値を更新する際に、SettingWithCopyWarningという警告が出ることがある。
例えば、次のようにユーザのデータを管理するDataFrameにおいて年齢(age)を更新する場合などである。

>>> import pandas as pd
>>> df = pd.DataFrame({'name':['ichiro','jiro','saburo'],'age':[30,25,20]},index=['01','02','03'])
>>> df
    age    name
01   30  ichiro
02   25    jiro
03   20  saburo
>>> df['age']['01'] = +1
__main__:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy

これは下記のページで説明されているように、df['age']['01']による要素へのアクセスは、2段階になっている。つまり、1段階目では、df['age']でage列から成るDataFrameを取得する。そして、2段階目で、age列のDataFrameの['01']行の要素にアクセスしている。すなわち、DataFrameを行列とみなした場合、行列の要素に直接アクセスしているのではなく、一度列ベクトルを作って、そして、その列ベクトルの所定の行にアクセスしているのである。もし、行数が多い場合は、行数分のメモリの確保とコピーに時間がかかる問題がある。
http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy

このSettingWithCopyWarningを回避するためには、.locを使うとよい。つまり、以下のようにすればよい。

>>> df.loc['01','age'] = +1
>>> df
    age    name
01   31  ichiro
02   25    jiro
03   20  saburo

これにより、DataFrameの要素に一段階でアクセスができ、処理が速くなる。また、DataFrameを行列と見なした場合に、行列の('01'、'age')要素にアクセスするのが、.loc['01','age']で書けるため、直感的にもあっているので良い。