为什么一次使用iloc()会给出SettingWithCopyWarning,而另一次却没有呢?

时间:2018-12-16 21:19:58

标签: python pandas copy

在类的方法中,我使用以下语句:

self.__datacontainer.iloc[-1]['c'] = value

这样做我得到一个 “ SettingWithCopyWarning: 试图在DataFrame的切片副本上设置一个值”

现在我试图重现此错误并编写以下简单代码:

import pandas, numpy
df = pandas.DataFrame(numpy.random.randn(5,3),columns=list('ABC'))
df.iloc[-1]['C'] = 3

我没有错误。为什么我在第一条语句而不是第二条语句中出现错误?

3 个答案:

答案 0 :(得分:2)

因此,在没有问题操作上下文的情况下很难回答这个问题,但是the pandas documentation很好地覆盖了这一点。

>>> df[['C']].iloc[0] = 2 # This is a problem
SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

基本上可以归结为-如果只能使用单个操作来完成索引操作,则不要将它们链接在一起。

>>> df.loc[0, 'C'] = 2 # This is ok

您收到的警告是因为您未能在要尝试修改的原始数据框中设置值-相反,您已将其复制并设置了一些内容到副本中(通常发生这种情况)对我来说,我什至没有该副本的引用,它只是被垃圾收集,因此警告非常有帮助)

答案 1 :(得分:2)

链索引

正如documentation和此站点上的其他几个答案([1][2])所建议的那样,链索引被认为是不好的做法,应避免使用。

因为似乎没有一种优美的方式来使用基于整数位置的索引(即.iloc)而不违反链索引的规则(从熊猫{{1 }}),建议您尽可能使用基于标签的索引(即v0.23.4)进行分配。

但是,如果您绝对需要按行号访问数据,则可以

.loc

df.iloc[-1, df.columns.get_loc('c')] = 42

熊猫的行为异常

根据我的理解,当您试图人为地再现错误时,绝对可以期待警告。

到目前为止,我发现它取决于数据帧的构造方式

df.iloc[[-1, 1], df.columns.get_indexer(['a', 'c'])] = 42

df = pd.DataFrame({'a': [4, 5, 6], 'c': [3, 2, 1]})
df.iloc[-1]['c'] = 42 # no warning

df = pd.DataFrame({'a': ['x', 'y', 'z'], 'c': ['t', 'u', 'v']})
df.iloc[-1]['c'] = 'f' # no warning

在分配链[3]时,熊猫(至少df = pd.DataFrame({'a': ['x', 'y', 'z'], 'c': [3, 2, 1]}) df.iloc[-1]['c'] = 42 # SettingWithCopyWarning: ... )似乎对混合类型和单一类型数据帧的处理方式不同

v0.23.4

这对我来说确实很奇怪,尽管我不确定这是否是不可预期的。

但是,有一个旧的bug具有类似的行为。


更新

根据developers,上述行为是可以预期的。

答案 2 :(得分:2)

不要专注于警告。警告只是一个指示,有时甚至不会出现when you expect it should。有时您会注意到它occurs inconsistently。相反,只需避免chained indexing或通常使用可能是副本的方式。

您希望通过行整数位置列标签建立索引。鉴于熊猫具有通过整数位置或标签(但不能同时 )进行索引的功能,所以这是不自然的混合。

在这种情况下,您可以通过单个iat调用对行和列使用整数位置索引

df.iat[-1, df.columns.get_loc('C')] = 3

或者,如果您的索引标签得到了保证唯一,则可以使用at

df.at[df.index[-1], 'C'] = 3