如何更正此代码以不引发 SettingWithCopyWarning?

How to correct this code to not raise a SettingWithCopyWarning?

提问人:TransitoryGouda 提问时间:6/16/2023 最后编辑:mkrieger1TransitoryGouda 更新时间:9/11/2023 访问量:34

问:

我正在关注这个:https://www.kdnuggets.com/2021/01/cleaner-data-analysis-pandas-pipes.html

大约在一半的时候,作者创建了一个函数来删除异常值:

def to_category(df):
    cols = df.select_dtypes(include='object').columns
    for col in cols:
        ratio = len(df[col].value_counts()) / len(df)
        if ratio < 0.05:
            df[col] = df[col].astype('category')
    return df

这引起了 Python 的警告:

Warning (from warnings module):
  File "D:/I7_Education/pandas_pipe_function1/pipes3.py", line 51
    df[col] = df[col].astype('category')
SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

我不确定我是否理解问题所在(尽管我正在努力解决它,并且在网上发布了一些帖子以试图理解)。我仍在尝试理解文档解释。

我知道我可以禁止来自 Python 的警告(如果您禁止警告,代码运行良好)。我想知道如何更改文章中的代码,以便它首先不会发出警告。

我尝试联系作者,但没有收到回复。

我想要的是没有必要压制。但是我不明白问题是什么,以至于无法弄清楚如何更改代码以首先不触发 SettingWithCopyWarning。

我没想到会发出警告。文档以及一些在线帖子说使用 loc 更改 df,但我不是在数据帧中更改值或元素,而是将列的 dtype 从对象更改为类别; 是如何做到这一点的,我认为遍历列来做到这一点应该没问题。一个朋友告诉我创建一个传递给函数的 df 副本,然后操作它,然后返回副本,我也不完全理解,但它并没有解决问题 - 它仍然会引发相同的警告。astype('catagory')

我传递给函数的数据帧是一个副本。本文只是在操作数据集(directmarketing.csv);它将 CSV 读取到 Pandas DataFrame 中并直接对其进行操作。相反,我创建了两个数据帧:第一个是,第二个是,我只是在操作营销数据帧。这样,我就可以回过头来检查数据集数据帧,并确保事情已经按照预期的方式发生了变化,等等。dataset = pd.read_csv(".\directmarketing.csv")marketing = dataset.copy()

但是当我调用该函数时,我正在调用 - 我根本没有接触数据集数据帧。to_category(marketing)

stackoverflow 上有一个线程 - 使用 Python pandas 数据帧时返回副本与视图警告 - 谈到了这一点,但它说要制作副本以避免警告,所以我非常困惑。

有没有办法更正文章中的代码,使其不会触发此警告?

我正在使用 Python 3.10 和 Idle - 我没有使用 IDE。

Python pandas 警告

评论

0赞 NotAName 6/16/2023
你为什么不使用文档中索引数据帧的正确方法?loc
0赞 jezrael 6/16/2023
你能解释一下吗?您确定需要除以每个长度的每列的唯一值数吗?一样喜欢?len(df[col].value_counts()) / len(df)(~df[col].duplicated()).mean()
0赞 TransitoryGouda 6/16/2023
我。。。不明白你的问题?这岂不是违背了函数的重点,然后是文章?我以为需要知道列的名称或其位置吗?对不起 - 我不知道如何回复具体评论。我确定有办法,我只是没有使用stackoverflow足够多,不知道怎么做。loc

答:

0赞 jezrael 6/16/2023 #1

一个想法是通过 DataFrame.astype 重写解决方案,列表中的列名通过以下方式转换为字典:finaldict.fromkeys

def to_category(df):
    final = []
    cols = df.select_dtypes(include='object').columns
    for col in cols:
        ratio = len(df[col].value_counts()) / len(df)
        if ratio < 0.05:
            final.append(col)
    return df.astype(dict.fromkeys(final, 'category'))

评论

0赞 TransitoryGouda 6/16/2023
这行得通。谢谢!...现在我必须弄清楚为什么这有效。我了解 return 语句之前的所有内容。它生成列名列表。dict.fromkeys 正在制作一个字典,其中键是列名,每个键的值是“category”。然后 astype 将这些特定的列 dtypes 更改为关联的值。是吗?我不知道你可以将字典传递给 astype - 我认为它必须是字符串。非常感谢你!
0赞 jezrael 6/16/2023
@TransitoryGouda - 完全正确,通过列表创建具有相同键和值的字典 - 因此转换为仅在字典中指定的分类列,不触及 anothr 列。dict.fromkeys(final, 'category')categoryfinalreturn df.astype(dict.fromkeys(final, 'category'))