Python 获取 SettingWithCopyWarning - iloc 与 loc - 无法弄清楚原因
Python getting SettingWithCopyWarning - iloc vs. loc - cannot figure out why
我对 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
和 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
,因为它正在尝试转换数据框的副本并将这些更改也应用到原始数据框。
我做了一些探索,根据我的理解,这就是 SettingWithCopyWarning
的本质:每次从另一个帧 df_orig
创建数据帧 df
时, pandas
采用一些启发式方法来确定数据 是否可能被隐式 从 df_orig
复制,而经验不足的用户可能不会意识到这一点。如果是这样,df
的 _is_copy
字段将设置为 df_orig
的 weak reference。稍后,当尝试就地更新 df
时,pandas
将根据 df._is_copy
以及 [= 的其他一些字段来确定是否应显示 SettingWithCopyWarning
16=](注意 df._is_copy
是 不是唯一的标准 。但是,由于某些方法在不同场景之间共享,因此启发式方法并不完美,并且某些情况可能会处理不当。
在 post 的代码中,housing.loc[train_index]
和 housing.iloc[train_index]
return 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) 在需要就地更新时,在切片后制作数据的 "explicit" 副本。在您的示例中,方法 (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()
is another method that can be used to make an "explicit" copy. If you intend to change one or more columns of df
, use df.assign(col=...)
创建副本而不是 df["col"]=...
。
我对 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
和 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
,因为它正在尝试转换数据框的副本并将这些更改也应用到原始数据框。
我做了一些探索,根据我的理解,这就是 SettingWithCopyWarning
的本质:每次从另一个帧 df_orig
创建数据帧 df
时, pandas
采用一些启发式方法来确定数据 是否可能被隐式 从 df_orig
复制,而经验不足的用户可能不会意识到这一点。如果是这样,df
的 _is_copy
字段将设置为 df_orig
的 weak reference。稍后,当尝试就地更新 df
时,pandas
将根据 df._is_copy
以及 [= 的其他一些字段来确定是否应显示 SettingWithCopyWarning
16=](注意 df._is_copy
是 不是唯一的标准 。但是,由于某些方法在不同场景之间共享,因此启发式方法并不完美,并且某些情况可能会处理不当。
在 post 的代码中,housing.loc[train_index]
和 housing.iloc[train_index]
return 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) 在需要就地更新时,在切片后制作数据的 "explicit" 副本。在您的示例中,方法 (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()
is another method that can be used to make an "explicit" copy. If you intend to change one or more columns of df
, use df.assign(col=...)
创建副本而不是 df["col"]=...
。