文章目录(Table of Contents)
简介
我们在使用pandas的时候, settingwithcopywarning可能是最最常见遇到的警告了. 在pandas中有许多索引的方式, 如果使用的不当就可能会出现这个warning.
这一篇文章主要说明这个错误是如何来的, 以及如何可以解决这个问题. 文章主要的内容来自于下面第二个参考链接. 我这里说的没有第二个链接里的内容详细, 关于一些更加深层次的原因我在这里没有过多的涉及, 可以在第二个链接中找到相应的解释.
参考资料
这是之前自己整理的内容: Python数据分析之Pandas
解决方法参考资料(主要就是参考的这个资料): SettingwithCopyWarning: How to Fix This Warning in Pandas
SettingwithCopyWarning来源分析以及解决
为了复现这个问题, 我们首先使用pandas来创建一些数据, 之后我们使用这些数据来进行实验.
- df = pd.DataFrame(np.random.randint(low=1, high=7, size=(5,4)),index=['a','b','c','d','e'],columns=['A','B','C','D'])
我们会生成如下的dataframe.
什么是SettingwithCopyWarning
首先我们要知道的是, 这是一个warning, 而不是一个error. 这个warning只是为了提醒你, 你的操作得到的结果可能和你预想的不一样, 你需要进行检查一下最终的结果.
下面我们深入来理解一下SettingwithCopyWarning. 首先我们来了解一下在pandas中的View和Copy.
在view中, df2只是原始的df1的一个子集. 而在copy中, df2是从df1中创建了一个新的object. 关于view和copy的区别, 下面这段文字解释的很好.
A view is a way of looking at a particular portion of the original data, whereas a copy is a clone of that data to a new location in memory.
于是, 在我们进行修改的时候, 就会出现一些潜在的问题.
在view中进行修改的时候, 如果修改了df2, 那么df1也是会进行改变的. 在copy中进行修改的话, 我们只修改了df2的值, df1的值是没有修改的. 这个就要看我们最终的目的是什么了.
知道了这些之后, 我们来看一下两个主要引起SettingwithCopyWarning的操作, 和如何进行fix.
Common issue #1: Chained assignment
我们首先看一个例子, 如何产生warning. 首先对于上面创建的dataframe, 我们找出列标为A的里面, 值为1的行, 如下所示.
- df[df.A==1]
此时, 我们希望进行update, 我们希望把D的部分改为3. 于是我们使用下面的方式来进行操作:
- df[df.A==1]['D']=3
此时, 就会出现warning. 我们看一下最终的值是否有改变.
- # 这个时候我们重新进行查看, 可以看到值是没有改变的
- df[df.A==1]
下面来解释一下这个warning的原因, 和没有改变的原因.
这里产生warning的原因是我们在上面希望进行update的时候, 使用了两次中括号, 我们实际上只对中间变量进行了修改. 当然使用其他的索引方式也会出现这样的问题, 例如使用.loc, .iloc等.
The same applies to any other form of chained call because we are generating this intermediate object.
没有改变的具体的原因如下:
上面我们的操作df[df.A==1]['D']=3可以分为下面两个部分进行操作.
- df[df.A==1]
- ['D']=3
这两个操作是顺序进行的, 也就是先进行第一个操作, 再进行第二个操作.
- 首先是第一个操作, 在进行df[df.A==1]之后, 会返回一个dataframe.
- 而第二个操作是在第一个操作生成的dataframe上进行修改的, 而不是在原始的dataframe上进行修改的, 所以最终的结果是没有改变的.
那么解决的方法也是很容易的, 我们只需要把上面两步的操作合并到一个步骤中就可以了. 我们使用loc来完成相应的操作(关于loc的操作, 可以查看链接: Pandas索引指南).
- df.loc[df.A==1,'D']=3
这样, 我们就完成了dataframe中值的更新.
Common issue #2: Hidden chaining
接着我们介绍第二个容易导致SettingwithCopyWarning的原因. 这个概括起来就是我们取了原始的dataframe的子集, 然后对子集进行操作, 就会出现这样的warning. 我们下面还是看一个例子. (这里的值可能和上面有点不一样, 我重新random了一下)
- df_sub=df[df.A==df.B]
- df_sub
接着我们对df_sub进行操作, 例如对其中的一个值进行修改, 此时会出现warning, 如下图所示.
- # loc先行号, 再列号
- df_sub.loc['d','D']=6
但是, 我们可以查看df_sub的结果, 可以看到实际上是修改成功的.
这个warning是在提示你, 你在修改df_sub的时候, 很可能也修改了df. 那么我们应该如何做来解决这个warning呢. 我们只需要在创建dataframe的时候使用copy就可以了. 我们下面看一下例子.
- df_sub=df[df.A==df.B].copycopy()
- # loc先行号, 再列号
- df_sub.loc['d','D']=6
- df_sub
可以看到此时是没有warning的.
一些更深层的原因
这里简单摘了一下参考文章里的内容, 我就不具体展开了.
在我们执行下面这段代码的时候, 程序实际上会进行翻译.
- # Our code
- data[data.bidder == 'parakeet2004']['bidderrate'] = 100
最后翻译之后会变为:
- # Code executed
- data.__getitem__(data.__getitem__('bidder') == 'parakeet2004').__setitem__('bidderrate', 100)
取子集的操作是getitem, 设置的操作是setitem. pandas无法保证getitem返回的是view还是copy, 这时候就会出现问题, 所以会出现warning.
In general, as discussed, pandas does not guarantee whether a get operation will return a view or a copy of the data. If a view is returned, the second expression in our chained assignment will be a call to setitem
on the original object.
But, if a copy is returned, it's the copy that will be modified instead – the original object does not get modified.
下面这段话解释的很明白:
The SettingWithCopyWarning
is letting us know that pandas cannot determine whether a view or a copy was returned by the first getitem
call, and so it’s unclear whether the assignment changed the original object or not. Another way to think about why pandas gives us this warning is because the answer to the question "are we modifying the original?" is unknown.
总之, 出现这个warning的时候, 不要只去忽视他, 尝试去解决这个warning.
- 微信公众号
- 关注微信公众号
- QQ群
- 我们的QQ群号
评论