没有SettingWithCopyWarning

时间:2016-09-04 22:07:25

标签: python pandas chained-assignment

well known(和understandable)在分配切片时,pandas行为基本上是不可预测的。但我过去常常通过SettingWithCopy警告来警告它。

为什么警告不会在以下两个代码段中生成,哪些技术可以减少无意中编写此类代码的可能性?

# pandas 0.18.1, python 3.5.1
import pandas as pd
data = pd.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c']})
new_data = data[['a', 'b']]
data = data['a']
new_data.loc[0, 'a'] = 100 # no warning, doesn't propagate to data

data[0] == 1
True


data = pd.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c']})
new_data = data['a']
data = data['a']
new_data.loc[0] = 100 # no warning, propagates to data

data[0] == 100
True

我认为解释是当父数据帧仍可从当前上下文访问时,pandas仅生成警告。 (这将是检测算法的一个弱点,正如我之前的例子所示。)

在下一个片段中,AFAIK原来的两列DataFrame不再可访问,但pandas警告机制设法触发(幸运的是):

data = pd.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c']})
new_data = data['a']
data = data[['a']]
new_data.loc[0] = 100 # warning, so we're safe

编辑:

在调查此事时,我发现另一个失踪警告案例:

data = pd.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c']})
data = data.groupby('a')
new_data = data.filter(lambda g: len(g)==1)
new_data.loc[0, 'a'] = 100 # no warning, does not propagate to data
assert data.filter(lambda g: True).loc[0, 'a'] == 1

即使几乎相同的例子确实会触发警告:

data = pd.DataFrame({'a': [1, 2, 2], 'b': ['a', 'b', 'c']})
data = data.groupby('a')
new_data = data.filter(lambda g: len(g)==1)
new_data.loc[0, 'a'] = 100 # warning, does not propagate to data
assert data.filter(lambda g: True).loc[0, 'a'] == 1

更新:我在这里回答了@firelynx的回答,因为很难将其放在评论中。

在回答中,@ firelynx说第一个代码片段没有警告,因为我占用了整个数据帧。但即使我参与其中,我仍然没有收到警告:

# pandas 0.18.1, python 3.5.1
import pandas as pd
data = pd.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c'], c: range(3)})
new_data = data[['a', 'b']]
data = data['a']
new_data.loc[0, 'a'] = 100 # no warning, doesn't propagate to data

data[0] == 1
True

1 个答案:

答案 0 :(得分:2)

一步一步地解释你在做什么

您创建的Dataframe不是视图

data = pd.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c']})
data._is_view
False

new_data也不是视图,因为您正在使用所有列

new_data = data[['a', 'b']]
new_data._is_view
False

现在您要将数据指定为Series 'a'

data = data['a']
type(data)
pandas.core.series.Series

哪个是视图

data._is_view
True

现在您更新非副本new_data

中的值
new_data.loc[0, 'a'] = 100 # no warning, doesn't propagate to data

这不应该发出警告。这是整个数据框架。

您创建的Series标记为视图,但它不是DataFrame,并且不像DataFrame视图那样。

避免像这样编写代码

系列与数据帧问题在熊猫中非常常见[如果你曾经使用过一段时间的熊猫,则不需要引用]

问题是你应该一直写作

data[['a']]不是data['a']

Left创建一个数据框视图,右边创建一个系列。

有些人可能会争辩永远不会写data['a']而是会data.a。因此,您可以为您的环境添加警告data['a']代码。

这不起作用。首先使用data.a语法会导致认知失调。

数据框是列的集合。在python中,我们使用[]运算符访问集合的成员。我们通过.运算符访问属性。切换这些会导致任何python程序员的认知失调。特别是当你开始做del data.a之类的事情并注意到它不起作用时。 See this answer for more extensive explaination

清理救援代码

很难看出data[['a']]data['a']

之间的区别

这是一种气味。我们应该既不

使用干净代码原则和蟒蛇的正确方法“明确比隐含更好”

就是这样:

columns = ['a']
data[columns]

这可能不是那么令人难以置信,但请看下面的例子:

data[['ad', 'cpc', 'roi']]

这是什么意思?这些专栏是什么?你在这里得到什么数据?

在阅读这行代码时,这是第一个到达任何人头脑的问题。

如何解决?不要发表评论。

ad_performance_columns = ['ad', 'cpc', 'roi']
data[ad_performance_columns]

更明确的总是更好。

有关更多信息,请考虑购买干净代码的书籍。也许this one