【问题标题】:Python getting SettingWithCopyWarning - iloc vs. loc - cannot figure out whyPython 得到 SettingWithCopyWarning - iloc 与 loc - 无法弄清楚原因
【发布时间】: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后是否尝试过重置索引?如果您正在解析一个子集,特别是如果您正在创建/更新/计算同一数据帧中的新值,该数据帧已被切割成原始子集,则该警告往往会出现。

标签: python pandas view copy warnings


【解决方案1】:

这里的问题不是因为索引,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,因为它正在尝试转换数据框的副本并将这些更改也应用于原始数据框。

【讨论】:

    【解决方案2】:

    我做了一些探索,根据我的理解,这就是SettingWithCopyWarning的底层内容:每次从另一个帧df_orig创建一个数据帧df时,pandas采用一些启发式方法来确定数据是否可能隐含df_orig 复制,经验不足的用户可能不知道。如果是这样,则将df_is_copy 字段设置为df_origweak 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 的更新是否会影响原始数据框 housingFalse 结果表明基础数据已被复制。但是,对于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"]=... 创建副本。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-07-17
      • 1970-01-01
      • 2018-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多