max*_*max 15 python pandas chained-assignment
它是众所周知的(和理解)分配到切片时,大熊猫的行为本质上是不可预测的.但我常常被警告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
Run Code Online (Sandbox Code Playgroud)
我认为解释是,当父数据帧仍可从当前上下文访问时,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
Run Code Online (Sandbox Code Playgroud)
编辑:
在对此进行调查时,我发现了另一个缺失警告的情况:
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
Run Code Online (Sandbox Code Playgroud)
即使几乎相同的示例确实触发警告:
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
Run Code Online (Sandbox Code Playgroud)
更新:我在这里回答@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
Run Code Online (Sandbox Code Playgroud)
您创建的数据框不是视图
data = pd.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c']})
data._is_view
False
Run Code Online (Sandbox Code Playgroud)
new_data 也不是视图,因为您正在获取所有列
new_data = data[['a', 'b']]
new_data._is_view
False
Run Code Online (Sandbox Code Playgroud)
现在您正在将数据分配为Series 'a'
data = data['a']
type(data)
pandas.core.series.Series
Run Code Online (Sandbox Code Playgroud)
哪个是视图
data._is_view
True
Run Code Online (Sandbox Code Playgroud)
现在您更新非复制中的值new_data
new_data.loc[0, 'a'] = 100 # no warning, doesn't propagate to data
Run Code Online (Sandbox Code Playgroud)
这不应该发出警告。它是整个数据框。
您Series创建的将自身标记为视图,但它不是 DataFrame,并且不表现为 DataFrame 视图。
Series 与 Dataframe 问题在 pandas 中是一个非常常见的问题[如果您已经使用 pandas 一段时间,则不需要引用]
问题实际上是你应该一直写作
data[['a']]不是data['a']
左边创建一个数据框视图,右边创建一个系列。
有些人可能会争辩说永远不要写,而是data['a']写data.a。因此,您可以向data['a']代码环境添加警告。
这是行不通的。首先,使用data.a语法会导致认知失调。
数据框是列的集合。在Python中,我们使用运算符访问集合的成员[]。我们通过操作符访问属性.。对于任何一个 Python 程序员来说,改变这些都会导致认知失调。特别是当你开始做类似的事情del data.a并发现它不起作用时。请参阅此答案以获得更广泛的解释
很难看出data[['a']]和之间的区别data['a']
这是一种气味。我们两者都不应该做。
使用干净代码原则和Python禅宗“显式优于隐式”的正确方法
这是:
columns = ['a']
data[columns]
Run Code Online (Sandbox Code Playgroud)
这可能不是那么令人难以置信,但看看下面的例子:
data[['ad', 'cpc', 'roi']]
Run Code Online (Sandbox Code Playgroud)
这是什么意思?这些列是什么?你在这里得到什么数据?
这是任何人在阅读这行代码时首先想到的问题。
怎么解决呢?不说评论。
ad_performance_columns = ['ad', 'cpc', 'roi']
data[ad_performance_columns]
Run Code Online (Sandbox Code Playgroud)
更明确总是更好。
如需更多信息,请考虑购买一本关于干净代码的书。也许这个
| 归档时间: |
|
| 查看次数: |
424 次 |
| 最近记录: |