python settingwithcopywarning_如何在Pandas中处理SettingWithCopyWarning?

如何应对SettingWithCopyWarning熊猫?

这篇文章适合读者,

想了解这个警告意味着什么

想了解抑制此警告的不同方法

想了解如何改进他们的代码并遵循良好做法以避免将来出现此警告。

建立

np.random.seed(0)

df = pd.DataFrame(np.random.choice(10, (3, 5)), columns=list('ABCDE'))

df

A  B  C  D  E

0  5  0  3  3  7

1  9  3  5  2  4

2  7  6  8  8  1

什么是SettingWithCopyWarning?

要知道如何处理这个警告,重要的是要了解它的含义以及为什么它首先被提出。

过滤DataFrame时,可以对一个帧进行切片/索引以返回视图或副本,具体取决于内部布局和各种实现细节。正如术语所暗示的,“视图”是原始数据的视图,因此修改视图可以修改原始对象。另一方面,“复制”是原始数据的复制,修改副本对原始数据没有影响。

正如其他答案所提到的那样,SettingWithCopyWarning创建了标记“链式赋值”操作。请考虑df上面的设置。假设您要选择“B”列中的所有值,其中“A”列中的值> 5.Pandas允许您以不同的方式执行此操作,其中一些更正确。例如,

df[df.A > 5]['B']

1    3

2    6

Name: B, dtype: int64

和,

df.loc[df.A > 5, 'B']

1    3

2    6

Name: B, dtype: int64

这些返回相同的结果,因此如果您只是读取这些值,则没有任何区别。那么,问题是什么?链式赋值的问题在于,通常很难预测是否返回了视图或副本,因此当您尝试返回值时,这很大程度上成为一个问题。要构建前面的示例,请考虑解释器如何执行此代码:

df.loc[df.A > 5, 'B'] = 4

# becomes

df.__setitem__((df.A > 5, 'B'), 4)

只需一次__setitem__通话即可df。OTOH,请考虑以下代码:

df[df.A > 5]['B'] = 4

# becomes

df.__getitem__(df.A > 5).__setitem__('B", 4)

现在,根据是__getitem__返回视图还是复制,__setitem__操作可能无效。

通常,您应该使用loc基于标签的赋值,以及iloc基于整数/位置的赋值,因为规范保证它们始终在原始操作上运行。此外,要设置单个单元格,您应该使用at和iat。

更多信息可以在文档中找到。

注意完成的

所有布尔索引操作loc也可以使用iloc。唯一的区别是iloc期望索引的整数/位置或布尔值的numpy数组,以及列的整数/位置索引。

例如,

df.loc[df.A > 5, 'B'] = 4

可以写成nas

df.iloc[(df.A > 5).values, 1] = 4

和,

df.loc[1, 'A'] = 100

可以写成

df.iloc[1, 0] = 100

等等。

告诉我如何压制警告!

考虑对“A”列的简单操作df。选择“A”并除以2将引发警告,但操作将起作用。

df2 = df[['A']]

df2['A'] /= 2

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/IPython/__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

df2

A

0  2.5

1  4.5

2  3.5

有几种方法可以直接消除此警告:

做一个 deepcopy

df2 = df[['A']].copy(deep=True)

df2['A'] /= 2

更改pd.options.mode.chained_assignment

可以设置为None,"warn"或"raise"。"warn"是默认值。None将完全抑制警告,"raise"并将抛出一个SettingWithCopyError,以防止操作通过。

pd.options.mode.chained_assignment = None

df2['A'] /= 2

@Peter Cotton在评论中提出了一种很好的方式,即使用上下文管理器非侵入式地改变模式(从这个要点修改),只在需要时设置模式,并将其重置为完成后的原始状态。

class ChainedAssignent:

def __init__(self, chained=None):

acceptable = [None, 'warn', 'raise']

assert chained in acceptable, "chained must be in " + str(acceptable)

self.swcw = chained

def __enter__(self):

self.saved_swcw = pd.options.mode.chained_assignment

pd.options.mode.chained_assignment = self.swcw

return self

def __exit__(self, *args):

pd.options.mode.chained_assignment = self.saved_swcw

用法如下:

# some code here

with ChainedAssignent():

df2['A'] /= 2

# more code follows

或者,提出异常

with ChainedAssignent(chained='raise'):

df2['A'] /= 2

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

“XY问题”:我做错了什么?

很多时候,用户试图寻找抑制此异常的方法,而不是完全理解为什么它首先被提出。这是XY问题的一个很好的例子,用户试图解决问题“Y”,这实际上是更深层根问题“X”的症状。将根据遇到此警告的常见问题提出问题,然后将提供解决方案。

问题1

我有一个DataFrame

df

A  B  C  D  E

0  5  0  3  3  7

1  9  3  5  2  4

2  7  6  8  8  1

我想在col“A”> 5到1000中分配值。我的预期输出是

A  B  C  D  E

0     5  0  3  3  7

1  1000  3  5  2  4

2  1000  6  8  8  1

错误的方法:

df.A[df.A > 5] = 1000         # works, because df.A returns a view

df[df.A > 5]['A'] = 1000      # does not work

df.loc[df.A  5]['A'] = 1000   # does not work

正确使用方式loc:

df.loc[df.A > 5, 'A'] = 1000

问题2 1

我试图将单元格(1,'D')中的值设置为12345.我的预期输出是

A  B  C      D  E

0  5  0  3      3  7

1  9  3  5  12345  4

2  7  6  8      8  1

我尝试了不同的方法来访问这个单元格,例如  df['D'][1]。做这个的最好方式是什么?

1.此问题与警告没有特别关系,但了解如何正确执行此特定操作以避免将来可能出现警告的情况是很好的。

您可以使用以下任何方法执行此操作。

df.loc[1, 'D'] = 12345

df.iloc[1, 3] = 12345

df.at[1, 'D'] = 12345

df.iat[1, 3] = 12345

问题3

我试图根据某些条件对值进行子集化。我有一个DataFrame

A  B  C  D  E

1  9  3  5  2  4

2  7  6  8  8  1

我想将“D”中的值分配给123,使“C”== 5.我试过了

df2.loc[df2.C == 5, 'D'] = 123

这似乎很好,但我仍然得到了  SettingWithCopyWarning!我该如何解决?

这实际上可能是因为您的管道中的代码更高。你是df2从更大的东西创造的,比如

df2 = df[df.A > 5]

?在这种情况下,布尔索引将返回一个视图,因此df2将引用原始。你需要做的是分配df2一份副本:

df2 = df[df.A > 5].copy()

# Or,

# df2 = df.loc[df.A > 5, :]

问题4

我试图将“C”列放在原位

A  B  C  D  E

1  9  3  5  2  4

2  7  6  8  8  1

但是使用

df2.drop('C', axis=1, inplace=True)

投掷SettingWithCopyWarning。为什么会这样?

这是因为df2必须已经创建为来自其他切片操作的视图,例如

df2 = df[df.A > 5]

这里的解决方案是要么做copy()的df,或使用loc,如前。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值