这个错误是在对pandas模块不熟悉的时候会经常遇见的. 我现在的处理方法就是有赋值行为之前, 先避免使用切片方法. 不过本文作为一种整理, 有必要对这种报错的底层逻辑进行明晰. 以下将用一个例子详细说明.
初始化一个用于举例的df
df = pd.DataFrame(np.arange(12).reshape(2,6).T, columns=['a','b'])
df
a b
0 0 6
1 1 7
2 2 8
3 3 9
4 4 10
5 5 11
df1 = df[df.a%2==1]
df1
a b
1 1 7
3 3 9
5 5 11
对df的切片df1的某个值进行操作, 出现本文标题的报错. 当然, 由于是warning级别的报错, 程序还是运行了, 只是不推荐.
df1.iloc[0,1]=77
D:\ProgramData\Anaconda3\lib\site-packages\pandas\core\indexing.py:1732: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
self._setitem_single_block(indexer, value, name)
D:\ProgramData\Anaconda3\lib\site-packages\pandas\core\indexing.py:723: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
iloc._setitem_with_indexer(indexer, value, self.name)
现在将df2命名为一个新的非切片df, 再重复上述操作. 可见此次操作赋值成功.
df2 = pd.DataFrame(df[df.a%2==1])
df2
a b
1 1 7
3 3 9
5 5 11
df2.iloc[0,1]=77
df2
a b
1 1 77
3 3 9
5 5 11
根据以上的例子, 我们应该已经明白个大概了. 实际上, 从python语句的理解上考虑, 要明白以下几点:
df1报错是因为
df[df.a%2==1]
本身是一个切片后返回的原df的子集. 这一点基于pandas对view与copy的区分. 在pandas中, 某些操作有时会返回数据的视图, 也就是上面所说的子集, 又叫view, 有时会返回数据的副本, 也就是copy. view是原始数据的子集, copy则是在原始数据的基础上创建的一个新的对象.
而进行实际上等效于
df[df.a%2==1].iloc[0,1]=77
的操作时, 前者已经是df的一个view的子集, 后面再进行iloc函数求子集, 就会出现 A value is trying to be set on a copy of a slice from a DataFrame的情况.
所以, 我们要修改df的某个值时, 要一次性取到这个值的loc, 即行和列, 可以是按照index名和column名的loc函数也可以是按照index序数和column序数的iloc和iat函数.
最后, 奉上几个pandas官方文档给出的应用pandas时的建议: