轉自:https://blog.csdn.net/qq_42711381/article/details/90451301
由於剛好也遇到這個問題,記錄下來
使用的DataFrame的
當使用 frame2['year']['two'] = 10000, 即df名[列名][行名]的方式去賦值就會報錯, 提示如下
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
進入提示網頁, 查找與SettingWithCopyWarning有關部分, 這里簡單翻譯了一下(渣翻譯, 推薦大家去看原文, 在最后幾部分里)
chained indexing
這就是出現警告的原因, 我們在使用pandas中要極力避免出現chained index)
下面是一個例子解釋到底什么是chained indexing
In [4]: dfmi = pd.DataFrame([list('abcd'), list('efgh'), list('ijkl'), list('mnop')], ...: columns=pd.MultiIndex.from_product([['one', 'two'], ['first', 'second']])) ...: In [5]: dfmi Out[5]: one two first second first second 0 a b c d 1 e f g h 2 i j k l 3 m n o p
我們通過兩種不同的方式去訪問同一值
#第一種方式 In [6]: dfmi['one']['second'] Out[6]: 0 b 1 f 2 j 3 n Name: second, dtype: object #第二種方式 In [7]: dfmi.loc[:,('one', 'second')] Out[7]: 0 b 1 f 2 j 3 n Name: (one, second), dtype: object
可以看出雖然訪問方式不同, 但是返回的結果是相同的. 相同結果, 但其實第二種訪問方式應該是我們所推薦使用的, 原因如下
第一種訪問方式
使用dfmi['one']['second']其實是分為兩個獨立事件完成的, 一個事情接着一件事情發生:
第一步 執行dfmi['one']
第二步 在第一步的基礎上執行dfmi_with_one['second'], 相當於在第一步返回Series基礎上, 檢索索引['second']
看似是一步到位的訪問, 其實在內部調用了兩次__getitem__
第二種方式訪問
fmi.loc[:,('one', 'second')] 相當於將一個嵌套的元組(slice(None), ('one', 'second'))傳遞給一個__getitem__, 這就使得pandas將其作為一個整體來處理, 第二種方式比第一種方式速度更快
這里的第一種訪問方式就是chained indexing, 接下來解決為什么chained indexing會造成警告.
#第二種訪問方式(推薦方式) dfmi.loc[:, ('one', 'second')] = value #其實在編譯器中是這樣操作的 dfmi.iloc.setitem((slice(None), ('one', 'second')), value) #但是這一段代碼編譯器處理就很不同了 #第一種訪問方式chained indexing dfmi['one']['second'] = value #其實在編譯器中是這樣操作的 dfmi.__getitem__('one').__setitem__('second', value)
問題的關鍵就出在這里的__getitem__上, 因為我們很難預測到這里的__getitem__返回的是一個視圖或是一個copy, 因為我們無法確定__setitem))修改的到底是真實的dfmi或是暫時的copy副本, 這就是SettingWithCopy想要警告我們的.
到此問題就算是解決了, 出現警告的原因在於無法預測到底修改的是視圖還是副本.
注意
dfmi.loc保證是dfmi本身伴隨修改索引行為(這句話有點不太通順, 大家可以去看看原文, 重點是后面一句), 所以dfmi.loc.__getitem__和dfmi.loc__setitem__方法一定是直接作用在dfmi上的. 當然dfmi.loc.__getitem__(idx)就無法預測到時作用在視圖上或是副本上了.
另一種會出現這種警告的情形, 雖然這里並沒有明顯的鏈式索引.
In [9]: def do_something(df): ...: foo = df[['bar', 'baz']] ...: #對於foo是視圖或是副本, 其實我們是無法得知的 ...: foo['quux'] = value ...: return foo
解決警告的方案:
使用 DafaFrameming.loc[行名, 列名] = 值 的方式去賦值, 而不是使用DataFrame[][]的形式去賦值.