Python 得到 SettingWithCopyWarning - iloc 与 loc - 无法弄清楚原因
Posted
技术标签:
【中文标题】Python 得到 SettingWithCopyWarning - iloc 与 loc - 无法弄清楚原因【英文标题】:Python getting SettingWithCopyWarning - iloc vs. loc - cannot figure out why 【发布时间】:2019-05-30 10:24:01 【问题描述】:我对 SettingWithCopyWarning 有基本的了解,但我无法弄清楚为什么我会收到针对这种特殊情况的警告。
我正在关注https://github.com/ageron/handson-ml/blob/master/02_end_to_end_machine_learning_project.ipynb的代码
当我运行如下代码时(使用 .loc),我没有得到 SettingWithCopyWarning
但是,如果我改为使用 .iloc 运行代码,我会收到警告。
谁能帮我理解一下?
from sklearn.model_selection import StratifiedShuffleSplit
split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)
for train_index, test_index in split.split(housing, housing["income_cat"]):
strat_train_set = housing.loc[train_index]
strat_test_set = housing.loc[test_index]
for set_ in (strat_train_set, strat_test_set):
set_.drop("income_cat", axis=1, inplace=True)
【问题讨论】:
使用iloc
后是否尝试过重置索引?如果您正在解析一个子集,特别是如果您正在创建/更新/计算同一数据帧中的新值,该数据帧已被切割成原始子集,则该警告往往会出现。
【参考方案1】:
这里的问题不是因为索引,iloc
和 loc
在这里对您的工作方式相同。问题出在set_.drop("income_cat", axis=1, inplace=True)
。 set_
数据框与 strat_train_set
和 strat_test_set
之间似乎存在弱引用。
for set_ in (strat_train_set, strat_test_set):
print(set_._is_copy)
这样你得到:
<weakref at 0x128b30598; to 'DataFrame' at 0x128b355c0>
<weakref at 0x128b30598; to 'DataFrame' at 0x128b355c0>
这可能导致SettingWithCopyWarning
,因为它正在尝试转换数据框的副本并将这些更改也应用于原始数据框。
【讨论】:
【参考方案2】:我做了一些探索,根据我的理解,这就是SettingWithCopyWarning
的底层内容:每次从另一个帧df_orig
创建一个数据帧df
时,pandas
采用一些启发式方法来确定数据是否可能隐含从df_orig
复制,经验不足的用户可能不知道。如果是这样,则将df
的_is_copy
字段设置为df_orig
的weak reference。稍后,当尝试就地更新df
时,pandas
将根据df._is_copy
以及df
的一些其他字段确定是否应显示SettingWithCopyWarning
(注意df._is_copy
是这里不是唯一的标准)。但是,由于某些方法在不同场景之间共享,因此启发式方法并不完美,并且某些情况可能处理不当。
在帖子中的代码中,housing.loc[train_index]
和 housing.iloc[train_index]
都返回了 housing
数据帧的隐式副本。
for df in (housing.loc[train_index], housing.iloc[train_index]):
print(df._is_view, df._is_copy)
上述检查产生以下结果:
False None
False <weakref at 0x0000019BFDF37958; to 'DataFrame' at 0x0000019BFDF26550>
这里,_is_view
是另一个字段,它显示对 df
的更新是否会影响原始数据框 housing
。 False
结果表明基础数据已被复制。但是,对于housing.loc[train_index]
,df._is_copy
字段未设置,在我看来应该在这种情况下,导致SettingWithCopyWarning
丢失,当df
的就地修改由语句df.drop("income_cat", axis=1, inplace=True)
执行时.
为了避免SettingWithCopyWarning
,您需要(1)在切片之前执行就地更新; (2) 如果可能,将更新逻辑构建到切片中;或 (3) 当需要就地更新时,在切片后制作数据的“显式”副本。在您的示例中,方法 (1) 如下所示:
# Updates the housing data frame in-place before slicing
income_cat = housing["income_cat"]
housing.drop("income_cat", axis=1, inplace=True)
for train_index, test_index in split.split(housing, income_cat):
strat_train_set = housing.loc[train_index]
strat_test_set = housing.loc[test_index]
方法(2)如下所示:
feature_cols = housing.columns.difference(["income_cat"])
for train_index, test_index in split.split(housing, housing["income_cat"]):
# Filter columns at the same time as slicing the rows
strat_train_set = housing.loc[train_index, feature_cols]
strat_test_set = housing.loc[test_index, feature_cols]
方法(3)如下所示:
for train_index, test_index in split.split(housing, housing["income_cat"]):
...
for set_ in (strat_train_set, strat_test_set):
# Remove "inplace=True" results in a copy being made
set_.drop("income_cat", axis=1)
除了更改更新方法的inplace
设置之外,df.copy()
是另一种可用于制作“显式”副本的方法。如果您打算更改df
的一列或多列,请使用df.assign(col=...)
而不是df["col"]=...
创建副本。
【讨论】:
以上是关于Python 得到 SettingWithCopyWarning - iloc 与 loc - 无法弄清楚原因的主要内容,如果未能解决你的问题,请参考以下文章
没有 SettingWithCopyWarning 的不可预测的 pandas 切片分配行为
Python得到前面12个月的数据,Python得到现在时间 前一年的数据,
Python,phoenixdb - 没有得到一行(但也没有得到任何错误)