python笔记:SettingWithCopyWarning是什么怎么避免?

在代码中遇到错误提示:
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

一、问题来源

"""
当我在原有(运行OK)的代码基础上,添加把某列里的英文字母转换为小写的代码之后,出现了上述提示。
"""
import...

df = pd.read_csv('file_name.csv', encoding='gb18030', low_memory=False)
df_select = df.loc[:, ['column_1', 'column_2']] # 源文件字段虽多,但我们此刻只需要2列: column_1和column2。
df_final = df_select[(df_select['column_1'] == 'condition_1')] # 删选出满足条件condition_1的记录。

# 添加以下代码后再运行,代码就给挂起来了:
df_final['column_2'] = df_final['column_2'].astype('str').str.lower() # 把column_2里边含有的大写英文字母变成小写

二、问题分析总结

经过代码试验:发现应用.str.lower()&出现SettingWithCopyWarning的DataFrame有个特点:就是它应用了条件筛选df[df['column'] == 'condition'] & 它的名字改变了&我读取了>118行数据(不知道这点有没有普遍性)。 知道了这个特点后,以下代码就能正常运行:

import...

df = pd.read_csv('file_name.csv', encoding='gb18030', low_memory=False)
df_select = df.loc[:, ['column_1', 'column_2']]

"""把应用了条件筛选后的DataFrame保持原名称,即df_select,就没问题了:"""
df_select = df_select[(df_select['column_1'] == 'condition_1')] 

df_select['column_2'] = df_select['column_2'].astype('str').str.lower() # 如果column_2就是str,可以不用 .astype('str')

同理,下边的代码可正常运行:

df = pd.read_csv('file_name.csv', encoding='gb18030', low_memory=False)
df = df[(df['column_1'] == 'condition_1')] 
df['column_2'] = df['column_2'].str.lower()

但是这个代码不能正常运行,会出现SettingWithCopyWarning

df = pd.read_csv('file_name.csv', encoding='gb18030', low_memory=False)
df_final = df[(df['column_1'] == 'condition_1')]  # 不能运行的原因: 没有和应用条件筛选的DataFrame的名称, 即df,保持一致
df_final['column_2'] = df_final['column_2'].str.lower()

三、解决方法:

  • A方法:如上,保持使用.str.lower()的DataFrame名称和应用条件删选的DataFrame的一致。
    ---------------------------------更新-------------------------------------------
  • B方法:在条件删选代码后边使用.copy()就没有命名限制了,举个栗子:
df = pd.read_csv('file_name.csv', encoding='gb18030', low_memory=False)
df_final = df[(df['column_1'] == 'condition_1')].copy()
df_final['column_2'] = df_final['column_2'].str.lower()
  • C:如果你了解了它的原理,可以直接的&安全的沉默该设定:
pd.options.mode.chained_assignment = None # 默认是'warn'

总结:

  • SettingWithCopyWarning:本质上是一个‘Warning’,并不是一个‘Error’。
  • 它的作用就是提醒你,代码运行的结果有可能跟你预期的不一致,希望你能检查一下运行结果来确定一下。
  • 详细介绍请见链接Understanding SettingwithCopyWarning in pandas。

SettingWithCocy的来源历史(节选于Understanding SettingwithCopyWarning in pandas)。

我们或许会问:就不能明确一下chained indexing返回的是(DataFrame的)copy或者view(即df的局部),非得搞出来一个烧脑的·SettingWithCocy吗?这就需要回顾一下pandas的发展历史了。
pandas用来判断返回copy或者view的逻辑,来自于它的底层代码NumPy库(pandas就是NumPy的升级+改造)。返不返回view在NumPy里边是清晰的,pandas里的view也是经由NumPy来的。但是NumPy有个局限性,就是它的矩阵里边dtype限定为1种,而在一顿操作Numpy之后诞生的pandas不但能处理multi-dtype,还优化了计算的空间占用和处理条件。
但是凡事有得必有失,pandas只能在多功能(即处理multi-dtype)和保持NumPy效率之间做平衡*(插话:NumPy只接受一种dtype,它的底层语言是C语言,所以效率非常高)*。
在这个兼顾多功能和效率的过程中,pandas形成了一套异常复杂的规则 去判定返回view还是copy。有经验的pandas开发者一般都对它挺满意的,因为他们能轻松&正确的搞定它的index路径。
不幸的是新手几乎不可避免的要在这点碰上麻烦。此外,根据pandas的一个核心开发人员Jeff Reback“明人不说暗话,从语言的角度讲,直接探测chain indexing这事儿是不可能的,只能通过推测来实现。”
所以,从2013年年底发布的版本0.13.0中加入了warning statement,算是对这个无数开发者踩到的坑的回应。
在版本0.12.0及以前的版本中,ix是应用最广泛的引用函数。但是到了2013年年中,pandas开发组觉得是时候考虑一下菜鸟的感受了,所以就有了·ix·的替代者lociloc函数,它俩的功能更明晰更友好。
SettingWithCopyWarning此后也不断更新,在GitHub上热度很高,甚至目前也在不断更新,所以理解到这一点对成为pandas高手是必要的。

你可能感兴趣的:(Python,笔记)