Pandas - A value is trying to be set on a copy of a slice from a DataFrame(轉)

轉自:https://blog.csdn.net/qq_42711381/article/details/90451301

由於剛好也遇到這個問題,記錄下來

使用的DataFrame的

image

當使用 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[][]的形式去賦值.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章