在運行以下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 操作時,最好也避免使用鏈式索引。不僅因為鏈式操作較慢,而且因為鏈式索引始終是一個潛在的問題,只要你稍后進行賦值操作,就可能不會影響到原始對象,這可能不是你的預期操作,所以,請不惜一切代價避免使用鏈式索引。
參考文檔: