pandasのSettingWithCopyWarningについて
pandasを利用していると、SettingWithCopyWarningが出ることがあります。基本的には、参照渡しに起因する部分が原因で、DataFrameをcopy()メソッドによって、別のメモリに独立して作成すれば問題ないのですが、今回copy()を利用してもワーニングが解決出来ませんでした。
なぜこうなるかのは不明で、おそらくcopy()を利用した場合のワーニングは無視しても問題ないと思いますが、一応解決案をメモしておきます。
github
- githubのjupyter notebook形式のファイルはこちら
google colaboratory
- google colaboratory で実行する場合はこちら
筆者の環境
!sw_vers
ProductName: Mac OS X
ProductVersion: 10.14.6
BuildVersion: 18G6020
!python -V
Python 3.7.3
import pandas as pd
import numpy as np
# 6 x 2のDataFrameを作成します
df = pd.DataFrame(np.arange(12).reshape(6, 2), columns=['c0', 'c1'])
df
c0 | c1 | |
---|---|---|
0 | 0 | 1 |
1 | 2 | 3 |
2 | 4 | 5 |
3 | 6 | 7 |
4 | 8 | 9 |
5 | 10 | 11 |
locで条件に合う部分だけを抽出する形でオブジェクトを作成し、カラムを指定してから、ilocを用いて上書きしようとするとSettingWithCopyWarningが出現します。
これはよく見られるワーニングです。
df_1 = df[['c0']].loc[df['c0'] % 3 == 0]
df_1['c1'] = None
df_1['c1'].iloc[0] = 12
df_1.head()
/Users/hiroshi/anaconda3/lib/python3.7/site-packages/pandas/core/indexing.py:671: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
self._setitem_with_indexer(indexer, value)
c0 | c1 | |
---|---|---|
0 | 0 | 12 |
3 | 6 | None |
通常であれば、copy()メソッドを利用し、参照渡しではなく、別途メモリ上にオブジェクトを作成すればワーニングは消えます。しかし、この場合は消えません。
df_2 = df[['c0']].loc[df['c0'] % 3 == 0].copy()
df_2['c1'] = None
df_2['c1'].iloc[0] = 12
df_2.head()
/Users/hiroshi/anaconda3/lib/python3.7/site-packages/pandas/core/indexing.py:671: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
self._setitem_with_indexer(indexer, value)
c0 | c1 | |
---|---|---|
0 | 0 | 12 |
3 | 6 | None |
解決案
ilocで直接、行番号と列番号をしてすれば良いようです。 そのために、わざわざ columns.get_locメソッドを利用して、カラムのインデックス番号を取得する必要があります。
df_3 = df[['c0']].loc[df['c0'] % 3 == 0]
df_3['c1'] = None
idx = df_3.columns.get_loc('c1')
df_3.iloc[0, idx] = 12
df_3.head()
c0 | c1 | |
---|---|---|
0 | 0 | 12 |
3 | 6 | None |
まとめ
以上SettingWithCopyWarningの特殊な回避方法の紹介でした。ただ、私の感覚ですが、copy()メソッドを利用していれば問題ないでしょうし、pythonやpandasのバージョンによって挙動は変わると思います。
何かしらの参考になれば幸いです。