Pandas 中 SettingwithCopyWarning 的原理和解决方案
原文链接:https://www.dataquest.io/blog/settingwithcopywarning/
原文标题:Understanding SettingwithCopyWarning in pandas
原文发布时间:5 JULY 2017(需要注意时效性,文中有一些方法已经弃用,比如 ix)
学习 Python 数据分析的同学总是遇到这个警告,查询中文资料,一般只能找到个别的解决办法,不一定适用于自己遇到的情况。查到的最常见解决办法就是直接设置为不显示警告。这实际上并不能解决问题,搜索资料发现这篇英文讲解 SettingWithCopyWarning
原理非常系统的文章,翻译了一下,分享给大家。
太长不看
-
解决方案:学会识别链式索引,不惜一切代价避免使用链式索引
注意:如果你看不懂这里的解决方案,请阅读此文前半部分,直到真正理解如何去做
- 如果要更改原始数据,请使用单一赋值操作(
loc
):
data.loc[data.bidder == 'parakeet2004', 'bidderrate'] = 100
- 如果想要一个副本,请确保强制让 Pandas 创建副本:
winners = data.loc[data.bid == data.price].copy()
winners.loc[304, 'bidder'] = 'therealname'
-
强烈不推荐直接关闭警告,不过还是提供一下关闭警告的设置方法:
pd.set_option('mode.chained_assignment', None)
题图
SettingWithCopyWarning
是人们在学习 Pandas 时遇到的最常见的障碍之一。搜索引擎可以搜索到 Stack Overflow 上的问答、GitHub issues 和一些论坛帖子,分别提供了该警告在某些特定情况下的含义。会有这么多人同样遇到这个警告并不奇怪:有很多方法可以索引 Pandas 数据结构,每种数据结构都有各自的细微差别,甚至 Pandas 本身并不能保证两行代码的运行结果看起来完全相同。
本指南包含了生成警告的原因及解决方案,其中还包括一些底层细节,让你更好地了解代码内部的运行机制,最后提供了有关该话题的一些历史情况,解释代码底层以这样的方式运行的原因。
为了探索 SettingWithCopyWarning
,我们将使用 eBay 3 天拍卖出售的 Xbox 的价格数据集,该数据集出自 Modelling Online Auctions 一书。先来了解下数据的基本结构:
import Pandas as pd
data = pd.read_csv('xbox-3-day-auctions.csv')
data.head()
|
auctionid |
bid |
bidtime |
bidder |
bidderrate |
openbid |
price |
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 |
如你所见,数据集的每一行都是某一次 eBay Xbox 出价信息。下面是对数据集中每列的简要说明:
-
auctionid
- 每次拍卖的唯一标识符
-
bid
- 本次拍卖出价
-
bidtime
- 拍卖的时长,以天为单位,从投标开始累计
-
bidder
- 投标人的 eBay 用户名
-
bidderrate
- 投标人的 eBay 用户评级
-
openbid
- 卖方为拍卖设定的开标价
-
price
- 拍卖结束时的中标价
什么是 SettingWithCopyWarning?
首先要理解的是,SettingWithCopyWarning
是一个警告 Warning,而不是错误 Error。
错误表明某些内容是“坏掉”的,例如无效语法(invalid syntax)或尝试引用未定义的变量;警告的作用是提醒编程人员,他们的代码可能存在潜在的错误或问题,但是这些操作在该编程语言中依然合法。在这种情况下,警告很可能表明一个严重但不容易意识到的错误。
SettingWithCopyWarning
告诉你,你的操作可能没有按预期运行,需要检查结果以确保没有出错。
如果代码确实按预期工作,那么我们会很容易忽略该警告,但是 SettingWithCopyWarning
不应该被忽略。在进行下一步操作之前,我们需要花点时间了解这一警告显示的原因。
要了解 SettingWithCopyWarning
,首先要知道,Pandas 中的某些操作会返回数据的视图(View),某些操作会返回数据的副本(Copy)。
View VS Copy
如上所示,左侧的视图 df2
只是原始数据 df1
一个子集,而右侧的副本创建了一个新的对象 df2
。
当我们尝试对数据集进行更改时,这可能会出现问题:
修改视图或副本
根据需求,我们可能想要修改原始 df1
(左),也可能想要修改 df2
(右)。警告让我们知道,代码可能并没有符合需求,修改到的可能并不是我们想要修改的那个数据集。
稍后会深入研究这个问题,但是现在先来了解一下,警告出现的两个主要原因以及对应的解决方案。
链式赋值(Chained Assignment)
当 Pandas 检测到链式赋值(Chained Assignment)时会生成警告。为了方便后续的解释,先来解释一些术语:
- 赋值(Assignment) - 设置某些变量值的操作,例如
data = pd.read_csv('xbox-3-day-auctions.csv')
,有时会将这个操作称之为 设置(Set) 。
- 访问(Access) - 返回某些值的操作,具体参照下方的索引和链式索引示例。有时会将这个操作称之为 获取(Get) 。
- 索引(Indexing) - 任何引用数据子集的赋值或访问方法,例如
data[1:5]
。
- 链式索引(Chaining) - 连续使用多个索引操作,例如
data[1:5][1:3]
。
链式赋值是链式索引和赋值的组合。先快速浏览一下之前加载的数据集,稍后将详细介绍。在这个例子中,假设我们了解到用户 'parakeet2004'
的 bidderrate
值不正确,需要修改这个 bidderrate
值,那么先来查看一下用户 'parakeet2004'
的当前值:
data[data.bidder == 'parakeet2004']
|
auctionid |
bid |
bidtime |
bidder |
bidderrate |
openbid |
price |
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
字段,继续操作:
data[data.bidder == 'parakeet2004']['bidderrate'] = 100
/Library/Frameworks/Python.framework/Versions/36/lib/python3.6/ipykernel/__main__.py:1:SettingWithC