【问题标题】:Correct way to set new column in pandas DataFrame to avoid SettingWithCopyWarning在 pandas DataFrame 中设置新列以避免 SettingWithCopyWarning 的正确方法
【发布时间】:2017-02-21 23:16:20
【问题描述】:

试图在 netc df 中创建一个新列,但我收到警告

netc["DeltaAMPP"] = netc.LOAD_AM - netc.VPP12_AM

C:\Anaconda\lib\site-packages\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

在较新版本的 Pandas 中创建字段以避免收到警告的正确方法是什么?

pd.__version__
Out[45]:
u'0.19.2+0.g825876c.dirty'

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    正如错误中所说,尝试使用.loc[row_indexer,col_indexer] 创建新列。

    netc.loc[:,"DeltaAMPP"] = netc.LOAD_AM - netc.VPP12_AM.
    

    注意事项

    通过Pandas Indexing Docs,您的代码应该可以工作。

    netc["DeltaAMPP"] = netc.LOAD_AM - netc.VPP12_AM
    

    被翻译成

    netc.__setitem__('DeltaAMPP', netc.LOAD_AM - netc.VPP12_AM)
    

    应该具有可预测的行为。 SettingWithCopyWarning 仅用于警告用户在链式分配期间出现意外行为(这不是您正在做的事情)。但是,如文档中所述,

    有时在没有明显的链式索引进行时会出现SettingWithCopy 警告。这些是SettingWithCopy 旨在捕获的错误! Pandas 可能试图警告你你已经这样做了:

    然后,文档继续举例说明何时可能会出现该错误,即使它不是预期的。因此,如果没有更多上下文,我无法说出为什么会发生这种情况。

    【讨论】:

    • 我做了consistent_cnr.loc[:, 'num_weights'] = consistent_cnr.loc[:, 'name'].apply(apply_get_num_weights_biases).values,但一直收到这个“警告”。必须在导入后立即使用pd.options.mode.chained_assignment = None 压制它。
    • 现在它给了我 2 个警告而不是 1 个。代码:myframe.loc[:,'mynewcol'] = 1
    • 如果您的数据帧被过滤或切片,您需要在使用此答案之前重置索引:netc.reset_index(drop=True, inplace=True)。否则解决方案不起作用,您会收到其他 cmets 中描述的两个警告。
    • 我使用的是一个被切片的数据框,并且收到了各种警告。 @kotchwane 评论帮我弄清楚了。
    【解决方案2】:

    在将数据分配给通过索引构造的 DataFrame df 时,我遇到了 SettingWithCopyWarning 问题。 两个命令

    • df['new_column'] = something
    • df.loc[:, 'new_column'] = something

    没有警告就无法工作。 复制df (DataFrame.copy()) 后一切正常。

    在下面的代码中,比较df0 = df_test[df_test['a']>3]df1 = df_test[df_test['a']>3].copy()。 对于df0,这两个任务都会抛出警告。对于df1,两者都可以正常工作。

    >>> df_test
          a     b     c     d  e
    0   0.0   1.0   2.0   3.0  0
    1   4.0   5.0   6.0   7.0  1
    2   8.0   9.0  10.0  11.0  2
    3  12.0  13.0  14.0  15.0  3
    4  16.0  17.0  18.0  19.0  4
    >>> df0 = df_test[df_test['a']>3]
    >>> df1 = df_test[df_test['a']>3].copy()
    >>> df0['e'] = np.arange(4)
    __main__: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: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
    >>> df1['e'] = np.arange(4)
    >>> df0.loc[2, 'a'] = 77
    /opt/anaconda3/lib/python3.7/site-packages/pandas/core/indexing.py:1719: 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: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
      self._setitem_single_column(loc, value, pi)
    >>> df1.loc[2, 'a'] = 77
    >>> df0
          a     b     c     d  e
    1   4.0   5.0   6.0   7.0  0
    2  77.0   9.0  10.0  11.0  1
    3  12.0  13.0  14.0  15.0  2
    4  16.0  17.0  18.0  19.0  3
    >>> df1
          a     b     c     d  e
    1   4.0   5.0   6.0   7.0  0
    2  77.0   9.0  10.0  11.0  1
    3  12.0  13.0  14.0  15.0  2
    4  16.0  17.0  18.0  19.0  3
    

    顺便说一句:建议阅读有关此问题的文档(警告中的链接)

    【讨论】:

      【解决方案3】:

      您的示例不完整,因为它没有显示 netc 的来源。 netc 本身很可能是切片的产物,因此 Pandas 无法保证它不是视图或副本。

      例如,如果你这样做:

      netc = netb[netb["DeltaAMPP"] == 0]
      netc["DeltaAMPP"] = netc.LOAD_AM - netc.VPP12_AM
      

      那么 Pandas 将不知道 netc 是视图还是副本。如果它是单线,它实际上是这样的:

      netb[netb["DeltaAMPP"] == 0]["DeltaAMPP"] = netc.LOAD_AM - netc.VPP12_AM
      

      您可以更清楚地看到双重索引。

      如果您想让netcnetb 分开,一种可能的补救措施可能是在第一行强制复制(loc 是为了确保我们不会复制两次),例如:

      netc = netb.loc[netb["DeltaAMPP"] == 0].copy()
      

      另一方面,如果您想用新列修改netb,您可以这样做:

      netb.loc[netb["DeltaAMPP"] == 0, "DeltaAMPP"] = netc.LOAD_AM - netc.VPP12_AM
      

      【讨论】:

      • 检查上游以确定 df 的创建方式是我一直遇到的令人沮丧的微妙之处之一。这篇文章中的建议比其他任何人都更能帮助我解决这个问题。
      • 它也帮助我理解发生了什么。我一直想知道为什么我看到所有这些警告,而乍一看没有什么明显的;这些隐含的副作用会有所不同。谢谢。
      • 如果我想要最后一行的行为,但在这部分代码中无法访问netb(想想子函数的子函数),有什么我可以在不改变架构的情况下做的吗剧烈?
      【解决方案4】:

      正如其他答案中所指出的,您很有可能对数据进行了一些过滤,否则不应弹出此警告(因为您的步骤是正确的)。

      假设您已经进行了一些过滤,您可以尝试执行以下步骤:

      netc_copied = netc.copy()
      netc.loc[:, "DeltaAMPP"] = netc_copied["LOAD_AM"] - netc_copied["VPP12_AM"]
      

      请注意,我在原始 DataFrame 中添加了一个新列。您也可以在复制的 DataFrame 中执行此操作。

      【讨论】:

        【解决方案5】:

        您需要在创建列时重置索引,特别是如果您已过滤特定值...那么您不需要使用 .loc[row_indexer,col_indexer]

        netc.reset_index(drop=True, inplace=True)
        netc["DeltaAMPP"] = netc.LOAD_AM - netc.VPP12_AM
        

        然后它应该工作:)

        【讨论】:

        • 实际上,将索引重置与接受的解决方案相结合,为我解决了这个问题。所以你答案的第二行应该是netc.loc[:,"DeltaAMPP"] = netc.LOAD_AM - netc.VPP12_AM. 见我的评论above
        • 嗨 Kotchwane,我明白你的意思,甚至 netc.loc[:,"DeltaAMPP"] 也不会删除警告。然后你需要做的是将你的数据帧复制到另一个,警告将消失: PS:将数据帧复制到另一个你应该使用:import copy ==> new_netc = copy.deepcopy(netc) new_netc ["DeltaAMPP"] = new_netc.LOAD_AM - new_netc.VPP12_AM 正如我提到的那样,在您测试后感觉提出您的反馈:)
        【解决方案6】:

        一个更简单的解决方案是只使用'assign':

        netc = netc.assign(DeltaAMPP=netc_copied['LOAD_AM']-netc_copied['VPP12_AM'])
        

        【讨论】:

        • 根据文档,assign 返回一个新的数据框,所以我怀疑它在后台执行.copy()
        猜你喜欢
        • 2017-04-25
        • 2018-06-05
        • 2014-12-16
        • 2017-09-15
        • 1970-01-01
        • 2016-02-17
        • 2012-03-07
        • 2020-12-17
        • 1970-01-01
        相关资源
        最近更新 更多