<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Pandas 中 SettingwithCopyWarning 的原理和解決方案

      原文鏈接:https://www.dataquest.io/blog/settingwithcopywarning/
      原文標(biāo)題:Understanding SettingwithCopyWarning in pandas
      原文發(fā)布時(shí)間:5 JULY 2017(需要注意時(shí)效性,文中有一些方法已經(jīng)棄用,比如 ix
      作者:Benjamin Pryke
      譯者:Ivy Lee

      學(xué)習(xí) Python 數(shù)據(jù)分析的同學(xué)總是遇到這個(gè)警告,查詢中文資料,一般只能找到個(gè)別的解決辦法,不一定適用于自己遇到的情況。查到的最常見(jiàn)解決辦法就是直接設(shè)置為不顯示警告。搜索資料發(fā)現(xiàn)這篇英文講解 SettingWithCopyWarning 原理非常系統(tǒng)的文章,翻譯了一下,分享給大家。

      題圖

      SettingWithCopyWarning 是人們?cè)趯W(xué)習(xí) Pandas 時(shí)遇到的最常見(jiàn)的障礙之一。快速的網(wǎng)絡(luò)搜索可以搜索到 Stack Overflow 問(wèn)題,GitHub issues 和程序員的論壇帖子,試圖解釋這個(gè)警告在他們的特定情況下意味著什么。這么多人為此困擾并不奇怪:有很多方法可以索引 Pandas 數(shù)據(jù)結(jié)構(gòu),每種數(shù)據(jù)結(jié)構(gòu)都有自己獨(dú)特的細(xì)微差別,甚至 Pandas 本身并不能保證兩行代碼的運(yùn)行結(jié)果看起來(lái)完全相同。

      本指南解釋了生成警告的原因并展示了如何解決這一警告。它還包括一些底層的細(xì)節(jié),讓你更好地了解代碼內(nèi)部發(fā)生了什么,提供了有關(guān)該話題的一些歷史記錄,讓你了解為什么代碼底層以這樣的方式運(yùn)作。

      為了探索 SettingWithCopyWarning,我們將使用 Modelling Online Auctions 一書中的 eBay 3 天拍賣出售的 Xbox 的價(jià)格數(shù)據(jù)集。讓我們來(lái)看看:

      import Pandas as pd
      
      data = pd.read_csv('xbox-3-day-auctions.csv')
      data.head()
      
       auctionidbidbidtimebidderbidderrateopenbidprice
      0 8213034705 95.0 2.927373 jake7870 0 95.0 117.5
      1 8213034705 115.0 2.943484 davidbresler2 1 95.0 117.5
      2 8213034705 100.0 2.951285 gladimacowgirl 58 95.0 117.5
      3 8213034705 117.5 2.998947 daysrus 10 95.0 117.5
      4 8213060420 2.0 0.065266 donnie4814 5 1.0 120.0

      如你所見(jiàn),數(shù)據(jù)集的每一行都是某一次 eBay Xbox 出價(jià)信息。以下是該數(shù)據(jù)集中每列的簡(jiǎn)要說(shuō)明:

      • auctionid - 每次拍賣的唯一標(biāo)識(shí)符
      • bid - 本次拍賣出價(jià)
      • bidtime - 拍賣的時(shí)長(zhǎng),以天為單位,從投標(biāo)開(kāi)始累計(jì)
      • bidder - 投標(biāo)人的 eBay 用戶名
      • bidderrate - 投標(biāo)人的 eBay 用戶評(píng)級(jí)
      • openbid - 賣方為拍賣設(shè)定的開(kāi)標(biāo)價(jià)
      • price - 拍賣結(jié)束時(shí)的中標(biāo)價(jià)

      什么是 SettingWithCopyWarning?

      首先要理解的是,SettingWithCopyWarning 是一個(gè)警告,而不是錯(cuò)誤 Error。

      錯(cuò)誤表明某些內(nèi)容是“壞掉”的,例如無(wú)效語(yǔ)法(invalid syntax)或嘗試引用未定義的變量。警告的作用是提醒程序員,他們的代碼可能存在潛在的錯(cuò)誤或問(wèn)題,但是這些操作仍然是該編程語(yǔ)言中的合法操作。在這種情況下,警告很可能表明一個(gè)嚴(yán)重但不容易意識(shí)到的錯(cuò)誤。

      SettingWithCopyWarning 告訴你,你的操作可能沒(méi)有按預(yù)期運(yùn)行,你應(yīng)該檢查結(jié)果以確保沒(méi)有出錯(cuò)。

      如果你的代碼仍然按預(yù)期工作,那么很容易忽略警告。這不是良好的實(shí)踐,SettingWithCopyWarning 不應(yīng)該被忽略。在采取下一步行動(dòng)之前,花點(diǎn)時(shí)間了解為什么會(huì)獲得這一警告。

      要了解 SettingWithCopyWarning,首先需要了解 Pandas 中的某些操作可以返回?cái)?shù)據(jù)的視圖(View),而某些其他操作將返回?cái)?shù)據(jù)的副本(Copy)。

      View VS Copy

      如上所示,左側(cè)的視圖 df2 只是原始 df1 一個(gè)子集,而右側(cè)的副本創(chuàng)建了一個(gè)新的唯一對(duì)象 df2

      當(dāng)我們嘗試對(duì)數(shù)據(jù)集進(jìn)行更改時(shí),這可能會(huì)導(dǎo)致問(wèn)題:

      修改視圖或副本

      根據(jù)我們的需求,我們可能想要修改原始 df1(左),可能想要修改 df2(右)。警告讓我們知道,我們的代碼可能并沒(méi)有符合需求,修改的并不是我們想要修改的那個(gè)數(shù)據(jù)集。

      我們稍后會(huì)深入研究這個(gè)問(wèn)題,但是現(xiàn)在先來(lái)了解一下,警告出現(xiàn)的兩個(gè)主要原因以及如何解決它們。

      鏈?zhǔn)劫x值(Chained assignment)

      當(dāng) Pandas 檢測(cè)鏈?zhǔn)劫x值(Chained assignment)時(shí)會(huì)生成警告。讓我們定義一些術(shù)語(yǔ),方便后續(xù)的解釋:

      • 賦值(Assignment) - 設(shè)置某些變量值的操作,例如 data = pd.read_csv('xbox-3-day-auctions.csv') 。也被稱為設(shè)置(set)
      • 訪問(wèn)(Access) - 返回某些值的操作,例如下面的索引和鏈?zhǔn)剿饕纠R脖环Q為獲取(get)
      • 索引(Indexing) - 引用數(shù)據(jù)子集的任何賦值或訪問(wèn)方法,例如 data[1:5]
      • 鏈?zhǔn)剿饕–haining) - 連續(xù)使用多個(gè)索引操作,例如data[1:5][1:3]

      鏈?zhǔn)劫x值是鏈?zhǔn)剿饕唾x值的組合。先快速瀏覽一下之前加載的數(shù)據(jù)集,稍后我們將詳細(xì)介紹這一點(diǎn)。在這個(gè)例子中,假設(shè)我們了解到用戶 'parakeet2004' 的 bidderrate 不正確,我們必須修改他的 bidderrate,首先,查看一下當(dāng)前的值。

      data[data.bidder == 'parakeet2004']
      
       auctionidbidbidtimebidderbidderrateopenbidprice
      6 8213060420 3.00 0.186539 parakeet2004 5 1.0 120.0
      7 8213060420 10.00 0.186690 parakeet2004 5 1.0 120.0
      8 8213060420 24.99 0.187049 parakeet2004 5 1.0 120.0

      我們有三行要更新 bidderrate 字段,我們繼續(xù)往下操作:

      data[data.bidder == 'parakeet2004']['bidderrate'] = 100
      
      /Library/Frameworks/Python.framework/Versions/36/lib/python3.6/ipykernel/__main__.py:1:SettingWithCopyWarning: 
      A value is trying to be set on a copy of a slice from aDataFrame.
      Try using .loc[row_indexer,col_indexer] = value instead
      See the caveats in the documentation:http://Pandas.pydata.org/Pandas-docs/stable/indexinghtml#indexing-view-versus-copy
        if __name__ == '__main__':
      

      不好了!我們神奇的造成了 SettingWithCopyWarning

      如果檢查一下,可以看到在這種情況下,值沒(méi)有按預(yù)期改變:

      data[data.bidder == 'parakeet2004']
      
       auctionidbidbidtimebidderbidderrateopenbidprice
      6 8213060420 3.00 0.186539 parakeet2004 5 1.0 120.0
      7 8213060420 10.00 0.186690 parakeet2004 5 1.0 120.0
      8 8213060420 24.99 0.187049 parakeet2004 5 1.0 120.0

      生成警告是因?yàn)槲覀儗蓚€(gè)索引操作鏈接在一起,我們直接使用了兩次方括號(hào),所以這比較容易理解。但如果我們使用其他訪問(wèn)方法,例如 .bidderrate.loc[].iloc[].ix[],也是如此,我們的鏈?zhǔn)讲僮魇牵?/p>

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

      這兩個(gè)鏈?zhǔn)讲僮饕粋€(gè)接一個(gè)地獨(dú)立執(zhí)行。第一次是訪問(wèn)操作(get),返回一個(gè) DataFrame,其中包含所有 bidder 等于 'parakeet2004' 的行。第二個(gè)是賦值操作(set),是在這個(gè)新的 DataFrame 上運(yùn)行的,我們壓根沒(méi)有在原始 DataFrame 上運(yùn)行。

      這個(gè)解決方案很簡(jiǎn)單:使用 loc 將鏈?zhǔn)讲僮鹘M合到一個(gè)操作中,以便 Pandas 可以確保 set 的是原始 DataFrame。Pandas 會(huì)始終確保下面這樣的非鏈?zhǔn)?set 操作起作用。

      # 設(shè)置新值
      data.loc[data.bidder == 'parakeet2004', 'bidderrate'] = 100
      # 檢查結(jié)果
      data[data.bidder == 'parakeet2004']['bidderrate']
      
      
      6    100
      7    100
      8    100
      Name: bidderrate, dtype: int64
      

      這就是警告中建議我們做的操作,在這種情況下它完美地適用。

      隱蔽的鏈?zhǔn)讲僮鳎℉idden chaining)

      現(xiàn)在來(lái)看一下遇到 SettingWithCopyWarning 的第二種最常見(jiàn)的方式。我們來(lái)探索中標(biāo)者的數(shù)據(jù),我們將為此創(chuàng)建一個(gè)新的 DataFrame,現(xiàn)在已經(jīng)學(xué)習(xí)了關(guān)于鏈?zhǔn)劫x值的內(nèi)容,因此請(qǐng)注意使用 loc

      winners = data.loc[data.bid == data.price]
      winners.head()
      
       auctionidbidbidtimebidderbidderrateopenbidprice
      3 8213034705 117.5 2.998947 daysrus 10 95.00 117.5
      25 8213060420 120.0 2.999722 djnoeproductions 17 1.00 120.0
      44 8213067838 132.5 2.996632 *champaignbubbles* 202 29.99 132.5
      45 8213067838 132.5 2.997789 *champaignbubbles* 202 29.99 132.5
      66 8213073509 114.5 2.999236 rr6kids 4 1.00 114.5

      我們可能會(huì)使用 winners 變量編寫一些后續(xù)的代碼行。

      mean_win_time = winners.bidtime.mean()
      ... # 20 lines of code
      mode_open_bid = winners.openbid.mode()
      

      偶然的機(jī)會(huì),我們?cè)谠?DataFrame 發(fā)現(xiàn)了另一個(gè)錯(cuò)誤:標(biāo)記為 304 的行中缺少了 bidder 值。

      winners.loc[304, 'bidder']
      
      nan
      

      對(duì)這個(gè)例子來(lái)說(shuō),假設(shè)我們知道這個(gè)投標(biāo)人的真實(shí)用戶名,并以此更新數(shù)據(jù):

      winners.loc[304, 'bidder'] = 'therealname'
      
      /Library/Frameworks/Python.framework/Versions/36/lib/python3.6/Pandas/core/indexing.py:517:SettingWithCopyWarning: 
      A value is trying to be set on a copy of a slice from aDataFrame.
      Try using .loc[row_indexer,col_indexer] = value instead
      
      See the caveats in the documentation: http://Pandas.pydata.org/Pandas-docs/stable/indexing.html#indexing-view-versus-copy
        self.obj[item] = s
      

      另一個(gè) SettingWithCopyWarning!但我們使用了 loc,這又是怎么回事?為了研究這一點(diǎn),我們來(lái)看看代碼的結(jié)果:

      print(winners.loc[304, 'bidder'])
      
      therealname
      

      代碼按預(yù)期工作了,為什么我們還是得到警告?

      鏈?zhǔn)剿饕赡芸缭絻尚写a發(fā)生,也可能在一行代碼內(nèi)發(fā)生。因?yàn)?winners 是作為 get 操作的輸出創(chuàng)建的(data.loc[data.bid == data.price]),它可能是原始 DataFrame 的副本,也可能不是,但除非我們檢查,否則我們不能了解到。當(dāng)我們對(duì) winners 進(jìn)行索引時(shí),我們實(shí)際上使用的是鏈?zhǔn)剿饕?/p>

      這意味著當(dāng)我們嘗試修改 winners 時(shí),我們可能也修改了 data

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

      為了防止這種情況下的警告,解決方案是在創(chuàng)建新 DataFrame 時(shí)明確告知 Pandas 制作一個(gè)副本:

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

      就這么簡(jiǎn)單!

      竅門就是,學(xué)會(huì)識(shí)別鏈?zhǔn)剿饕幌б磺写鷥r(jià)避免使用鏈?zhǔn)剿饕H绻脑紨?shù)據(jù),請(qǐng)使用單一賦值操作。如果你想要一個(gè)副本,請(qǐng)確保你強(qiáng)制讓 Pandas 制作副本。這樣可以節(jié)省時(shí)間,也可以使代碼保持嚴(yán)密的邏輯。

      另外請(qǐng)注意,即使 SettingWithCopyWarning 只在你進(jìn)行 set 時(shí)才會(huì)發(fā)生,但在進(jìn)行 get 操作時(shí),最好也避免使用鏈?zhǔn)剿饕f準(zhǔn)讲僮鬏^慢,而且只要你稍后決定進(jìn)行賦值操作,就會(huì)導(dǎo)致問(wèn)題。

      處理 SettingWithCopyWarning 的提示和技巧

      在我們進(jìn)行下面更深入的分析之前,讓我們“拿出顯微鏡”,看看 SettingWithCopyWarning 的更多細(xì)節(jié)。

      關(guān)閉警告

      首先,如果不討論如何明確地控制 SettingWithCopy 設(shè)置,那么本文則不完整。Pandas 的 mode.chained_assignment 選項(xiàng)可以采用以下幾個(gè)值之一:

      • 'raise' - 拋出異常(exception)而不是警告
      • 'warn' - 生成警告(默認(rèn))
      • None - 完全關(guān)閉警告

      例如,如果要關(guān)閉警告:

      pd.set_option('mode.chained_assignment', None)
      data[data.bidder == 'parakeet2004']['bidderrate'] = 100
      

      因?yàn)檫@樣沒(méi)有給我們?nèi)魏尉妫悄阃耆私庾约涸谧鍪裁矗駝t不建議這樣做。如果你對(duì)想要實(shí)現(xiàn)的操作有任何一丁點(diǎn)的疑問(wèn),關(guān)閉警告都不被推薦。有些開(kāi)發(fā)者非常重視 SettingWithCopy 甚至選擇將其提升為異常,如下所示:

      pd.set_option('mode.chained_assignment', 'raise')
      data[data.bidder == 'parakeet2004']['bidderrate'] = 100
      
      ---------------------------------------------------------------------------
      SettingWithCopyError                      Traceback (most recent call last)
      <ipython-input-13-80e3669cab86> in <module>()
            1 pd.set_option('mode.chained_assignment', 'raise')
      ----> 2 data[data.bidder == 'parakeet2004']['bidderrate'] = 100
      
      /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/Pandas/core/frame.py in __setitem__(self, key, value)
          2427         else:
         2428             # set column
      -> 2429             self._set_item(key, value)
         2430 
         2431     def _setitem_slice(self, key, value):
      
      /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/Pandas/core/frame.py in _set_item(self, key, value)
         2500         # value exception to occur first
         2501         if len(self):
      -> 2502             self._check_setitem_copy()
          2503 
         2504     def insert(self, loc, column, value, allow_duplicates=False):
      
      /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/Pandas/core/generic.py in _check_setitem_copy(self, stacklevel, t, force)
         1758 
         1759             if value == 'raise':
      -> 1760                 raise SettingWithCopyError(t)
         1761             elif value == 'warn':
         1762                 warnings.warn(t, SettingWithCopyWarning, stacklevel=stacklevel)
      
      SettingWithCopyError: 
      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
      
      See the caveats in the documentation: http://Pandas.pydata.org/Pandas-docs/stable/indexing.html#indexing-view-versus-copy
      

      如果你正在與缺乏經(jīng)驗(yàn)的 Pandas 開(kāi)發(fā)人員合作開(kāi)發(fā)項(xiàng)目,或者正在開(kāi)發(fā)需要高度嚴(yán)謹(jǐn)?shù)捻?xiàng)目,這可能特別有用。

      使用此設(shè)置的更精確方法是使用 上下文管理器 context manager

      # resets the option we set in the previous code segment
      pd.reset_option('mode.chained_assignment')
      
      with pd.option_context('mode.chained_assignment', None):
          data[data.bidder == 'parakeet2004']['bidderrate'] = 100
      

      正如你所看到的,這種方法可以實(shí)現(xiàn)針對(duì)性的警告設(shè)置,而不是影響整個(gè)環(huán)境。

      is_copy 屬性

      避免警告的另一個(gè)技巧是修改 Pandas 用于解釋 SettingWithCopy 的工具之一。每個(gè) DataFrame 都有一個(gè) is_copy 屬性,默認(rèn)情況下為 None,但如果它是副本,則會(huì)使用 weakref 引用原始 DataFrame 。通過(guò)將 is_copy 設(shè)置為 None,可以避免生成警告。

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

      但是請(qǐng)注意,這并不會(huì)奇跡般地解決問(wèn)題,反而會(huì)使錯(cuò)誤檢測(cè)變得非常困難。

      單類型 VS 多類型對(duì)象

      值得強(qiáng)調(diào)的另一點(diǎn)是單類型對(duì)象和多類型對(duì)象之間的差異。如果 DataFrame 所有列都具有相同的 dtype,則它是單類型的,例如:

      import numpy as np
      
      single_dtype_df = pd.DataFrame(np.random.rand(5,2), columns=list('AB'))
      print(single_dtype_df.dtypes)
      single_dtype_df
      
      A    float64
      B    float64
      dtype: object
      
       AB
      0 0.383197 0.895652
      1 0.077943 0.905245
      2 0.452151 0.677482
      3 0.533288 0.768252
      4 0.389799 0.674594

      如果 DataFrame 的列不是全部具有相同的 dtype,那么它是多類型的,例如:

      multiple_dtype_df = pd.DataFrame({'A': np.random.rand(5),'B': list('abcde')})
      print(multiple_dtype_df.dtypes)
      multiple_dtype_df
      
      A    float64
      B     object
      dtype: object
      
       AB
      0 0.615487 a
      1 0.946149 b
      2 0.701231 c
      3 0.756522 d
      4 0.481719 e

      由于下面歷史部分中所述的原因,對(duì)多類型對(duì)象的索引 get 操作將始終返回副本。然而,為了提高效率,索引器對(duì)單類型對(duì)象的操作幾乎總是返回一個(gè)視圖,這里需要注意的是,這取決于對(duì)象的內(nèi)存布局,并不能完全保證。

      誤報(bào)

      誤報(bào),即無(wú)意中報(bào)告鏈?zhǔn)劫x值的情況,曾經(jīng)在早期版本的 Pandas 中比較常見(jiàn),但此后大部分都被解決了。為了完整起見(jiàn),在此處包括一些已修復(fù)的誤報(bào)示例也是有用的。如果你在使用早期版本的 Pandas 時(shí)遇到以下任何情況,則可以安全地忽略或抑制警告(或通過(guò)升級(jí)完全避免警告!)

      使用當(dāng)前列的值,將新列添加到 DataFrame 會(huì)生成警告,但這已得到修復(fù)。

      data['bidtime_hours'] = data.bidtime.map(lambda x: x * 24)
      data.head(2)
      
       auctionidbidbidtimebidderbidderrateopenbidpricebidtime_hours
      0 8213034705 95.0 2.927373 jake7870 0 95.0 117.5 70.256952
      1 8213034705 115.0 2.943484 davidbresler2 1 95.0 117.5 70.643616

      當(dāng)在一個(gè) DataFrame 切片上使用 apply 方法進(jìn)行設(shè)置時(shí),也會(huì)出現(xiàn)誤報(bào),不過(guò)這也已得到修復(fù)。

      data.loc[:, 'bidtime_hours'] = data.bidtime.apply(lambda x: x * 24)
      data.head(2)
      
       auctionidbidbidtimebidderbidderrateopenbidpricebidtime_hours
      0 8213034705 95.0 2.927373 jake7870 0 95.0 117.5 70.256952
      1 8213034705 115.0 2.943484 davidbresler2 1 95.0 117.5 70.643616

      最后,直到 0.17.0 版本前,DataFrame.sample 方法中存在一個(gè)錯(cuò)誤,導(dǎo)致 SettingWithCopy 警告誤報(bào)。現(xiàn)在,sample 方法每次都會(huì)返回一個(gè)副本。

      sample = data.sample(2)
      sample.loc[:, 'price'] = 120
      sample.head()
      
       auctionidbidbidtimebidderbidderrateopenbidpricebidtime_hours
      481 8215408023 91.01 2.990741 sailer4eva 1 0.99 120 71.777784
      503 8215571039 100.00 1.965463 lambonius1 0 50.00 120 47.171112

      鏈?zhǔn)劫x值深度解析

      讓我們重用之前的例子:試圖更新 databidder 值為 'parakeet2004' 的所有行的 bidderrate 字段。

      data[data.bidder == 'parakeet2004']['bidderrate'] = 100
      
      /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/ipykernel/__main__.py:1: 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
      
      See the caveats in the documentation: http://Pandas.pydata.org/Pandas-docs/stable/indexing.html#indexing-view-versus-copy
        if __name__ == '__main__':
      

      Pandas 用這個(gè) SettingWithCopyWarning 告訴我們的是,代碼的行為是模棱兩可的,但要理解為什么這樣做以及警告的措辭,以下概念將會(huì)有所幫助。

      我們之前簡(jiǎn)要地談過(guò)了視圖(View)和副本(Copy)。有兩種方法可以訪問(wèn) DataFrame 的子集:可以創(chuàng)建對(duì)內(nèi)存中原始數(shù)據(jù)的引用(視圖),也可以將子集復(fù)制到新的較小的 DataFrame 中(副本)。視圖是查看 原始 數(shù)據(jù)特定部分的一種方式,而副本是將該數(shù)據(jù)克隆到內(nèi)存中的新位置。正如我們之前的圖表所示,修改視圖將修改原始變量,但修改副本則不會(huì)。

      由于某些我們將在稍后介紹的原因,Pandas 中 get 操作的輸出無(wú)法保證。索引 Pandas 數(shù)據(jù)結(jié)構(gòu)時(shí),視圖或副本都可能被返回,這意味著對(duì)某一 DataFrame 進(jìn)行 get 操作返回一個(gè)新的 DataFrame ,這個(gè)新的數(shù)據(jù)可能是:

      • 來(lái)自原始對(duì)象的數(shù)據(jù)副本。
      • 沒(méi)有復(fù)制,而是直接對(duì)原始對(duì)象的引用。

      因?yàn)槲覀儾恢缹?huì)發(fā)生什么,并且每種可能性都有非常不同的行為,所以忽略警告就是“玩火”。

      為了更清楚地解釋視圖、副本和其中的歧義,讓我們創(chuàng)建一個(gè)簡(jiǎn)單的 DataFrame 并對(duì)其進(jìn)行索引:

      df1 = pd.DataFrame(np.arange(6).reshape((3,2)), columns=list('AB'))
      df1
      
       AB
      0 0 1
      1 2 3
      2 4 5

      df1 的子集賦值給 df2

      df2 = df1.loc[:1]
      df2
      
       AB
      0 0 1
      1 2 3

      根據(jù)剛才學(xué)到的知識(shí),我們知道 df2 可能是 df1 的視圖或 df1 子集的副本。

      在解決問(wèn)題之前,我們還需要再看一下鏈?zhǔn)剿饕U(kuò)展一下 'parakeet2004' 示例,我們將兩個(gè)索引操作鏈接在一起:

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

      __intermediate__ 表示第一個(gè)調(diào)用的輸出,對(duì)我們是完全不可見(jiàn)的。請(qǐng)記住,如果我們使用了屬性訪問(wèn),會(huì)得到相同的有問(wèn)題的結(jié)果:

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

      這同樣適用于任何其他形式的鏈?zhǔn)秸{(diào)用,因?yàn)槲覀冋谏芍虚g對(duì)象

      在底層代碼中,鏈?zhǔn)剿饕馕吨鴮?duì) __getitem____setitem__ 進(jìn)行多次調(diào)用以完成單個(gè)操作。這些是 特殊的 Python 方法,通過(guò)在實(shí)現(xiàn)它們的類的實(shí)例上使用方括號(hào),可以調(diào)用這些方法,這是語(yǔ)法糖的一種示例。讓我們看一下 Python 解釋器如何執(zhí)行我們示例中的內(nèi)容。

      # Our code
      data[data.bidder == 'parakeet2004']['bidderrate'] = 100
      
      # Code executed
      data.__getitem__(data.__getitem__('bidder') == 'parakeet2004').__setitem__('bidderrate', 100)
      

      正如你可能已經(jīng)意識(shí)到的那樣,SettingWithCopyWarning 是由此鏈?zhǔn)?__setitem__ 調(diào)用生成的。你可以自己嘗試一下 - 上面這些代碼的功能相同。為清楚起見(jiàn),請(qǐng)注意第二個(gè) __getitem__ 調(diào)用(對(duì) bidder 列)是嵌套的,而不是鏈?zhǔn)絾?wèn)題的所有部分。

      通常,如上面所述,Pandas 不保證 get 操作是返回視圖還是副本。如果在我們的示例中返回了一個(gè)視圖,則鏈?zhǔn)劫x值中的第二個(gè)表達(dá)式將是對(duì)原始對(duì)象 __setitem__ 的調(diào)用。但是,如果返回一個(gè)副本,那么將被修改的是副本 - 原始對(duì)象不會(huì)被修改。

      這就是警告中 “a value is trying to be set on a copy of a slice from a DataFrame” 的含義。由于沒(méi)有對(duì)此副本的引用,它最終將被回收SettingWithCopyWarning 讓我們知道 Pandas 無(wú)法確定第一個(gè) __getitem__ 調(diào)用是否返回了視圖或副本,因此不清楚該賦值是否更改了原始對(duì)象。換一種說(shuō)法就是:“我們是否正在修改原始數(shù)據(jù)?”這一問(wèn)題的答案是未知的。

      如果我們確實(shí)想要修改原始文件,警告建議的解決方案是使用 loc 將這兩個(gè)單獨(dú)的鏈?zhǔn)讲僮鬓D(zhuǎn)換為單個(gè)賦值操作。這樣我們的代碼中沒(méi)有了鏈?zhǔn)剿饕筒粫?huì)再收到警告。我們修改后的代碼及其擴(kuò)展版本如下所示:

      # Our code
      data.loc[data.bidder == 'parakeet2004', 'bidderrate'] = 100
      
      # Code executed
      data.loc.__setitem__((data.__getitem__('bidder') == 'parakeet2004', 'bidderrate'), 100)
      

      DataFrame 的 loc 屬性保證是原始 DataFrame 本身,具有擴(kuò)展的索引功能。

      假陰性(False negatives)

      使用 loc 并沒(méi)有結(jié)束我們的問(wèn)題,因?yàn)槭褂?loc 的 get 操作仍然可以返回一個(gè)視圖或副本。讓我們快速過(guò)一下,一個(gè)有點(diǎn)復(fù)雜的例子。

      data.loc[data.bidder == 'parakeet2004', ('bidderrate', 'bid')]
      
       bidderratebid
      6 100 3.00
      7 100 10.00
      8 100 24.99

      我們這次拉出了兩列而不是一列。讓我們嘗試 set 所有的 bid 值。

      data.loc[data.bidder == 'parakeet2004', ('bidderrate', 'bid')]['bid'] = 5.0
      data.loc[data.bidder == 'parakeet2004', ('bidderrate', 'bid')]
      
       bidderratebid
      6 100 3.00
      7 100 10.00
      8 100 24.99

      沒(méi)有效果,也沒(méi)有警告!我們?cè)谇衅母北旧?set 了一個(gè)值但是 Pandas 沒(méi)有檢測(cè)到它 - 這就是假陰性。這是因?yàn)椋褂?loc 之后并不意味著我們可以再次使用鏈?zhǔn)劫x值。這個(gè)特定的 bug,有一個(gè)未解決的 GitHub issue

      正確的解決方法如下:

      data.loc[data.bidder == 'parakeet2004', 'bid'] = 5.0
      data.loc[data.bidder == 'parakeet2004', ('bidderrate', 'bid')]
      
       bidderratebid
      6 100 5
      7 100 5
      8 100 5

      你可能懷疑,是否有人會(huì)在實(shí)踐中遇到這樣的問(wèn)題。其實(shí)這比你想象的更容易出現(xiàn)。當(dāng)我們像下一節(jié)中這樣做:將 DataFrame 查詢的結(jié)果賦值給變量。

      隱藏的鏈?zhǔn)剿饕?/h3>

      讓我們?cè)倏匆幌轮半[藏的鏈?zhǔn)剿饕纠覀冊(cè)噲D設(shè)置 winners 變量中,標(biāo)記為 304 行的 bidder 字段。

      winners = data.loc[data.bid == data.price]
      winners.loc[304, 'bidder'] = 'therealname'
      
      /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/Pandas/core/indexing.py:517: 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
      
      See the caveats in the documentation: http://Pandas.pydata.org/Pandas-docs/stable/indexing.html#indexing-view-versus-copy
        self.obj[item] = s
      

      我們得到了另一個(gè) SettingWithCopyWarning 盡管我們使用了 loc 。這個(gè)問(wèn)題可能令人非常困惑,因?yàn)榫嫘畔⒔ㄗh我們的方法,我們已經(jīng)做過(guò)了。

      不過(guò),想一下 winners 變量。它究竟是什么?由于我們通過(guò) data.loc[data.bid == data.price] 將它初始化,我們無(wú)法知道它是原始 data DataFrame 的視圖還是副本(因?yàn)?get 操作返回視圖或副本)。將初始化與生成警告的行組合在一起可以清楚地表明我們的錯(cuò)誤。

      data.loc[data.bid == data.price].loc[304, 'bidder'] = 'therealname'
      
      /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/Pandas/core/indexing.py:517: 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
      
      See the caveats in the documentation: http://Pandas.pydata.org/Pandas-docs/stable/indexing.html#indexing-view-versus-copy
        self.obj[item] = s
      

      我們?cè)俅问褂昧随準(zhǔn)劫x值,只是這次它被分在了兩行代碼中。思考這個(gè)問(wèn)題的另一種方法是,問(wèn)一個(gè)問(wèn)題“這個(gè)操作會(huì)修改一個(gè)對(duì)象,還是兩個(gè)對(duì)象?”在我們的示例中,答案是未知的:如果 winners 是副本,那么只有 winners 受到影響,但如果是視圖,則 winnersdata 都將被更新。這種情況可能發(fā)生在腳本或代碼庫(kù)中相距很遠(yuǎn)的行之間,這使問(wèn)題很難被追根溯源。

      此處警告的意圖是讓我們意識(shí)到,我們以為代碼將修改原始 DataFrame ,實(shí)際沒(méi)有修改成功,或者說(shuō)我們將修改副本而不是原始數(shù)據(jù)。深入研究 Pandas GitHub repo 中的 issue,你可以看到開(kāi)發(fā)人員自己對(duì)這個(gè)問(wèn)題的解釋。

      如何解決這個(gè)問(wèn)題在很大程度上取決于我們自己的意圖。如果我們想要使用原始數(shù)據(jù)的副本,解決方案就是強(qiáng)制 Pandas 制作副本。

      winners = data.loc[data.bid == data.price].copy()
      winners.loc[304, 'bidder'] = 'therealname'
      
      print(data.loc[304, 'bidder']) # Original
      print(winners.loc[304, 'bidder']) # Copy
      
      nan
      therealname
      

      另一方面,如果你需要更新原始 DataFrame ,那么你應(yīng)該使用原始 DataFrame 而不是重新賦值一些具有未知行為的其他變量。我們之前的代碼將修改為:

      # Finding the winners
      winner_mask = data.bid == data.price
      
      # Taking a peek
      data.loc[winner_mask].head()
      
      # Doing analysis
      mean_win_time = data.loc[winner_mask, 'bidtime'].mean()
      ... # 20 lines of code
      mode_open_bid = data.loc[winner_mask, 'openbid'].mode()
      
      # Updating the username
      data.loc[304, 'bidder'] = 'therealname'
      

      在更復(fù)雜的情況下,例如修改 DataFrame 子集的子集,不要使用鏈?zhǔn)剿饕梢栽谠?DataFrame 上通過(guò) loc 進(jìn)行修改。例如,你可以更改上面的新 winner_mask 變量或創(chuàng)建一個(gè)選擇中標(biāo)者子集的新變量,如下所示:

      high_winner_mask = winner_mask & (data.price > 150)
      data.loc[high_winner_mask].head()
      
       auctionidbidbidtimebidderbidderrateopenbidpricebidtime_hours
      225 8213387444 152.0 2.919757 uconnbabydoll1975 15 0.99 152.0 70.074168
      328 8213935134 207.5 2.983542 toby2492 0 0.10 207.5 71.605008
      416 8214430396 199.0 2.990463 volpendesta 4 9.99 199.0 71.771112
      531 8215582227 152.5 2.999664 ultimatum_man 2 60.00 152.5 71.991936

      這種技術(shù)會(huì)使得未來(lái)的代碼庫(kù)維護(hù)和擴(kuò)展更加穩(wěn)健。

      歷史

      你可能想知道為什么要造成這么混亂的現(xiàn)狀,為什么不明確指定索引方法是返回視圖還是副本,來(lái)完全避免 SettingWithCopy 問(wèn)題。要理解這一點(diǎn),我們必須研究 Pandas 的過(guò)去。

      Pandas 確定返回一個(gè)視圖還是一個(gè)副本的邏輯,源于它對(duì) NumPy 庫(kù)的使用,這是 Pandas 庫(kù)的基礎(chǔ)。視圖實(shí)際上是通過(guò) NumPy 進(jìn)入 Pandas 的詞庫(kù)的。實(shí)際上,視圖在 NumPy 中很有用,因?yàn)樗鼈兡軌蚩深A(yù)測(cè)地返回。由于 NumPy 數(shù)組是單一類型的,因此 Pandas 嘗試使用最合適的 dtype 來(lái)最小化內(nèi)存處理需求。因此,包含單個(gè) dtype 的 DataFrame 切片可以作為單個(gè) NumPy 數(shù)組的視圖返回,這是一種高效處理方法。但是,多類型的切片不能以相同的方式存儲(chǔ)在 NumPy 中。Pandas 兼顧多種索引功能,并且保持高效地使用其 NumPy 內(nèi)核的能力。

      最終,Pandas 中的索引被設(shè)計(jì)為有用且通用的方式,其核心并不完全與底層 NumPy 數(shù)組的功能相結(jié)合。隨著時(shí)間的推移,這些設(shè)計(jì)和功能元素之間的相互作用,導(dǎo)致了一組復(fù)雜的規(guī)則,這些規(guī)則決定了返回視圖還是副本。經(jīng)驗(yàn)豐富的 Pandas 開(kāi)發(fā)者通常都很滿意 Pandas 的做法,因?yàn)樗麄兛梢暂p松地瀏覽其索引行為。

      不幸的是,對(duì)于 Pandas 的新手來(lái)說(shuō),鏈?zhǔn)剿饕龓缀跏遣豢杀苊獾模驗(yàn)?get 操作返回的就是可索引的 Pandas 對(duì)象。此外,用 Pandas 的核心開(kāi)發(fā)人員之一 Jeff Reback 的話來(lái)說(shuō),“從語(yǔ)言的角度來(lái)看,直接檢測(cè)鏈?zhǔn)剿饕遣豢赡艿模仨毥?jīng)過(guò)推斷才能了解”。

      因此,在 2013 年底的 0.13.0 版本中引入了警告,作為許多開(kāi)發(fā)者遇到鏈?zhǔn)劫x值導(dǎo)致的無(wú)聲失敗的解決方案。

      在 0.12 版本之前,ix 索引器是最受歡迎的(在 Pandas 術(shù)語(yǔ)中,“索引器”比如 ixlociloc,是一種簡(jiǎn)單的結(jié)構(gòu),允許使用方括號(hào)來(lái)索引對(duì)象,就像數(shù)組一樣,但具有一些特殊的用法)。但是大約在 2013 年中 ,Pandas 項(xiàng)目開(kāi)始意識(shí)到日益增加的新手用戶的重要性,有動(dòng)力開(kāi)始提高新手用戶的使用體驗(yàn)。自從此版本發(fā)布以來(lái),lociloc 索引器因其更明確的性質(zhì)和更易于解釋的用法而受到青睞。(譯者注:pandas v0.23.3 (July 7, 2018),其中 ix 方法已經(jīng)被棄用

      Google Trends: Pandas

      SettingWithCopyWarning 在推出后持續(xù)改進(jìn),多年來(lái)在許多 GitHub issue 中得到了熱烈的討論 ,甚至還在不斷更新 ,但是要理解它,仍然是成為 Pandas 專家的關(guān)鍵。

      總結(jié)

      SettingWithCopyWarning 的基礎(chǔ)復(fù)雜性是 Pandas 庫(kù)中為數(shù)不多的坑。這個(gè)警告的源頭深深嵌在庫(kù)的底層中,不應(yīng)被忽視。Jeff Reback 自己的話 ,“我沒(méi)有找到任何你應(yīng)該忽略這個(gè)警告的情況。如果你做某些類型的索引時(shí)不起作用,而其他情況下起作用,你是在玩火。”

      幸運(yùn)的是,解決警告只需要識(shí)別鏈?zhǔn)劫x值并修復(fù)。如果整篇文章你只了解到了一件事,那么就應(yīng)該是這一點(diǎn)。



      作者:笨熊不緊張
      鏈接:https://www.jianshu.com/p/72274ccb647a
      來(lái)源:簡(jiǎn)書
      簡(jiǎn)書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請(qǐng)聯(lián)系作者獲得授權(quán)并注明出處。
      posted @ 2019-01-30 19:51  開(kāi)拓者亮仔  閱讀(1975)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 新巴尔虎右旗| 少妇太爽了在线观看免费视频 | 国内精品久久久久影院网站| 国产亚洲精品自在久久| 亚洲乱码中文字幕小综合| 精品偷拍一区二区三区在| 亚欧美闷骚院| 四虎国产精品免费久久| 丁香五月婷激情综合第九色| 日本妇人成熟免费| 亚洲最大av一区二区| 亚洲高清乱码午夜电影网| 镇平县| 夜夜影院未满十八勿进| 桃花岛亚洲成在人线AV| 国产精品一区中文字幕| 丁香婷婷色综合激情五月| 熟女女同亚洲女同中文字幕| 中国女人内谢69xxxx| 女同另类激情在线三区| 最近免费中文字幕大全免费版视频 | 中文有无人妻vs无码人妻激烈| 999国产精品999久久久久久 | 中文字幕日韩国产精品| 国产人妇三级视频在线观看| 另类图片亚洲人妻中文无码| 亚洲 一区二区 在线| 极品美女自拍偷精品视频| 亚洲成精品动漫久久精久| 国产又色又爽又黄的网站免费| 国产精品一区二区传媒蜜臀| 亚洲电影在线观看| 不卡一区二区三区视频播放 | 麻豆久久天天躁夜夜狠狠躁| 国产影片AV级毛片特别刺激| 国产在线观看播放av| 国产高清乱码又大又圆| 国产精品入口麻豆| 欧美粗大| 天堂影院一区二区三区四区| 久久精品国产中文字幕|