Python A value is trying to be set on a copy of a slice from a DataFrame

这个错误是在对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时的建议:

  • 使用向量化操作: 不用for遍历的Pandas方法和函数(因为这样更快)
  • 使用.apply方法
  • 使用.itertuples(): 将DataFrame行作为namedtuple类从Python的collections模块中进行迭代
  • 使用.iterrows(): 将DataFrame行作为(index, pd.Series)元组数组进行迭代. 虽然Pandas的Series是一种灵活的数据结构, 但将每一行生成一个Series并且访问它, 仍然是一个比较大的开销
  • 对逐个元素进行循环, 使用df.loc或者df.iloc对每个单元格或者行进行处理

你可能感兴趣的:(python,数据挖掘,开发语言)