众所周知(并且可以理解),当分配给切片时,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
我认为解释是熊猫仅在当前上下文仍可访问父 DataFrame 时才会产生警告。(这将是检测算法的一个弱点,正如我之前的示例所示。)
在下一个片段中,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