[python学习笔记] - Pandas的SettingwithCopy分析

警告信息

当我尝试修改dataframe或者对其赋值时,出现了警告信息:

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/pandasdocs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

分析

首先要明确,这是个警告而不是错误。是提醒你这段代码执行的结果可能不符合你的预期。
可以强行关闭警告,眼不见为净(不推荐):
pd.set_option('mode.chained_assignment', None)

  • raise - 抛出异常(exception)而不是警告
  • warn - 生成警告(默认)
  • None - 完全关闭警告

官方文档里有几个概念:

  1. Assignment,set,赋值。
  2. Access,get,取值。
  3. 索引(Indexing。任何引用数据子集的赋值或访问方法,例如 data[1:2]
  4. 链式索引(Chaining)。连续使用多个索引操作,例如data[1:2][1:2]
  5. Chained Assignment,链式赋值就是链式索引+赋值的组合。
  6. Hidden Chaining,隐式链式。
  7. view,视图,也可以理解为切片,slice。
  8. copy,副本,与原数据没有任何关联,存储在新的内存地址。

[python学习笔记] - Pandas的SettingwithCopy分析_第1张图片

Chained Assignment

请细品:

import pandas as pd
df = pd.DataFrame({
     "A":range(5), "B":range(5)})
# df
#    A  B
# 0  0  0
# 1  1  1
# 2  2  2
# 3  3  3
# 4  4  4
# try to set df[4, "B"] = 100

# method-1
df[df.A == 4]["B"] = 100
# 触发了1次SettingWithCopyWarning
# 第一个链式get,返回dataframe。
# 第二个链式set,在返回的datafram上修改。

# method-2
df.loc[df.A == 4, "B"] = 100
# df修改成功
# 使用loc将链式操作合并

# method-3
df["B"][df.A == 4] = 100
# df修改成功

lociloc来赋值可以避免链式赋值造成的警告和错误。

Hidden Chaining

有些时候,很多以为不是链式操作的代码其实是隐式链式。
请细品:

import pandas as pd
df = pd.DataFrame({
     "A":range(5), "B":range(5)})
# df
#    A  B
# 0  0  0
# 1  1  1
# 2  2  2
# 3  3  3
# 4  4  4

df1 = df[df.A >= 2] # view
df2 = df[df.A >= 2].copy() # copy
#   A  B
#2  2  2
#3  3  3
#4  4  4

# method-1.1
df1[df1.A == 4]["B"] = 100
# 触发了1次SettingWithCopyWarning

# method-1.2
df1.loc[df1.A == 4, "B"] = 100
# 触发了2次SettingWithCopyWarning

# method-1.3
df1["B"][df1.A == 4] = 100
# 触发了1次SettingWithCopyWarning

# method-2.1
df2[df2.A == 4]["B"] = 100
# 触发了1次SettingWithCopyWarning

# method-2.2
df2.loc[df2.A == 4, "B"] = 100
# 修改成功

# method-2.3
df2["B"][df2.A == 4] = 100
# 修改成功

由于df1df的视图,所以对df1的赋值仍要修改df,所以就变成了通过view的方式达隐式的实现了Chained Assignment。

链式操作,底层代码涉及到__getitem____setitem__,等价于:

df[df.A == 4]["B"] = 100
df.__getitem__(df.__getitem__('A') == 4).__setitem__('B', 100)
# df.__getitem__(df.__getitem__('A') == 4)为主体,调用__setitem__()

df.loc[df.A == 4, "B"] = 100
df.loc.__setitem__((df.__getitem__('A') == 4, 'B'), 100)
# df.loc为主体,调用__setitem__()

df["B"][df.A == 4] = 100
df.__getitem__('B').__setitem__(df.__getitem__('A') == 4, 100)
# df.__getitem__('B')为主体,调用__setitem__()

False negatives

详细分析见文末的参考链接。

结语

目前的分析只是皮毛,最有效的还是看官方文档,涉及到很多底层的实现。
后续会继续补全。

https://zhuanlan.zhihu.com/p/41202576 《Pandas SettingwithCopy 警告解决方案》

你可能感兴趣的:(Python,语法解析,学习笔记,pandas,SettingwithCopy)