pandas 學習 第12篇:DataFrame 避免鏈式賦值


在運行以下Python代碼時,Pandas拋出SettingWithCopyWarning警告:

row_data = df_pred.loc[key]
row_data['col'] = new_value

df_pred是一個數據框,根據索引從數據框中獲取一行,然后對該行的一個字段進行賦值,警告的詳細內容如下:

SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

該警告的意思是:在DataFrame的一個切片上的copy上進行賦值操作。出現警告是因為該賦值操作可能不會影響到原始的數據框。

從代碼上來理解:row_data 是原始數據框的一個切片(df_loc[key]),該切片可能是原始數據框的一個視圖(View),也可能是原始數據框的一個副本(Copy)。如果row_data是原始數據框的一個視圖,對row_data進行數據修改不會影響到df_pred。建議使用df_pred.loc[row_indx,col_index] 方式,該方式肯定修改原始數據框。

一,深拷貝和淺拷貝

在使用DataFrame修改數據時,要知道深拷貝和淺拷貝的區別。圖例描述了淺拷貝和深拷貝的區別,假設B復制了A,當修改B時,如果A也跟着變了,說明這是淺拷貝;如果A沒變,那就是深拷貝。

也就是說,淺拷貝是創建了對象的一個引用,而深拷貝是創建了對象的一個獨立的實體。

二,數據的視圖和副本

在pandas中,淺拷貝也稱作數據的視圖(View),深拷貝也稱作數據的副本(Copy),Pandas 中的某些操作可以返回數據的視圖(View),而某些其他操作將返回數據的副本(Copy)。

SettingWithCopyWarning 警告暗示:賦值操作可能沒有按照預期執行,你應該檢查結果以確保結果沒有出錯。

如果你的代碼執行的結果符合預期,那么你可以忽略警告,但這並不是良好的編碼實踐,SettingWithCopyWarning 警告不應該被忽略。在采取下一步行動之前,花點時間了解以下為什么Pandas會拋出這一警告。

三,警告出現的原因

直接拋出結論,警告出現的原因只有一個:

出現警告的原因是鏈式賦值

當 Pandas 檢測到鏈式賦值(Chained assignment)時會生成警告,鏈式賦值是指通過使用鏈式索引來賦值。鏈式索引(Chaining)是指連續使用多個索引,例如data[1:5][1:3]。

使用鏈式索引賦值的兩種方式:

  • 第一種是顯式的,索引是連續的,例如,data.iloc[1][3] = value
  • 第二種是隱式的,例如,先定義 df = data.iloc[1],再使用 df[3] = value 也屬於鏈式賦值

鏈式賦值可以在一行中進行,也可以跨越多行,只要Pandas檢測到鏈式賦值,Python就會拋出SettingWithCopyWarning警告。

1,顯式的鏈式賦值

舉個例子,使用鏈式索引對bidder列進行賦值:

data[data.bidder == 'parakeet2004']['bidderrate'] = 100

生成警告是因為我們把兩個索引操作鏈接在一起,代碼中直接使用了兩次中括號,所以這種格式比較容易理解。但如果我們使用其他訪問方法,例如 .bidderrate.loc[].iloc[].ix[],也是如此,我們的鏈式操作一個接一個獨立執行:

data[data.bidder == 'parakeet2004']
['bidderrate'] = 100

第一次是訪問操作(get),返回一個 DataFrame,其包含所有 bidder 等於 'parakeet2004' 的行。第二個是賦值操作(set),是在這個新的 DataFrame 上運行的,我們壓根沒有在原始 DataFrame 上運行。

這個解決方案很簡單:使用 loc 將鏈式操作組合到一個操作中,以便 Pandas 可以確保 set 操作是在原始 DataFrame上執行。Pandas 會始終確保下面這樣的非鏈式 set 操作起作用。

data.loc[data.bidder == 'parakeet2004', 'bidderrate'] = 100

2,隱式的鏈式賦值

舉個例子,使用原始數據框的一個變量,對變量賦值,這是隱式的鏈式賦值:

winners = data.loc[data.bid == data.price]
winners.loc[304, 'bidder'] = 'therealname'

隱式的鏈式索引可能跨越多行代碼發生,也可能在一行代碼內發生。因為 winners 是作為 get 操作的輸出創建的(data.loc[data.bid == data.price]),它可能是原始 DataFrame 的副本,也可能不是,但除非我們檢查,否則我們不能確認。當我們對 winners 進行索引時,我們實際上使用的是鏈式索引。這意味着當我們嘗試修改 winners 時,我們可能也修改了 data

在實際的代碼中,這些行可能會跨越很大的距離,因此追蹤問題可能會更困難,但情況是與示例類似的。

為了防止這種情況下的警告,解決方案是在創建新 DataFrame 時明確告知 Pandas 這是創建的一個副本,對副本的修改不會影響到原始對象:

winners = data.loc[data.bid == data.price].copy()
winners.loc[304, 'bidder'] = 'therealname'

 

敲門就是,學會識別鏈式索引,不惜一切代價避免使用鏈式索引。如果要更改原始數據,請使用單一索引賦值操作。如果你想要一個副本,請確保顯式調用copy()函數強制讓 Pandas 創建副本。

 

提一個中肯的建議,即使SettingWithCopyWarning警告只在set 時才會發生,但在進行 get 操作時,最好也避免使用鏈式索引。不僅因為鏈式操作較慢,而且因為鏈式索引始終是一個潛在的問題,只要你稍后進行賦值操作,就可能不會影響到原始對象,這可能不是你的預期操作,所以,請不惜一切代價避免使用鏈式索引。

 

 

 

參考文檔:

python學習筆記之Pandas 中 SettingwithCopyWarning 的原理和解決方案

SettingwithCopyWarning: How to Fix This Warning in Pandas


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM