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)

这里的问题不是因为索引,ilocloc 在这里对你来说是一样的。问题出在 set_.drop("income_cat", axis=1, inplace=True)set_ 数据框与 strat_train_setstrat_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_origweak 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 上的更新是否会影响原始数据帧 housingFalse 结果表明基础数据已被复制。但是,对于 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"]=...