数据治理 | 数据分析与清洗工具:Pandas 缺失值与重复值处理
我们将在数据治理板块中推出一系列原创推文,帮助读者搭建一个完整的社科研究数据治理软硬件体系(详情见文末)。
目录:
一、前言 二、缺失值 1. 缺失值处理方案 2. 如何识别缺失值 3. 使用 Pandas 处理缺失值
(1)删除法处理缺失值 (2)填充法处理缺失值 ① 使用 df.fillna()
② 使用 df.replace()
三、重复值 1. 如何检测重复值 2. 删除重复值 四、总结
Part1前言
Part2缺失值
1缺失值处理方案
NaN
、None
以及空字符''
、空格' '
、换行符 '\n'
等等。① 删除法,删除含有一定量缺失值的行或列 ② 填充法,使用其他值来替换缺失值 ③ 不处理,当作没看见
对于不同的数据、不同的使用需求,我们一般使用不同的方法来处理缺失值。
2如何识别缺失值
NaN
(下文会介绍什么是 NaN
)和 None
,Pandas 提供了便利的方法来识别他们,df.iana()
及其别名 df.isnull()
是 Pandas 中判断缺失值的主要方法。下表是某年中国工业企业数据的一小部分,我们以此表为例,使用 Pandas 为大家展示如何处理缺失值。import numpy as np
import pandas as pd
data = data = pd.read_excel('./数据/工业数据15条.xlsx')
data.loc[2, '行业大类名称'] = None # 插入空值 None
data.loc[7, '企业名称'] = '' # 插入空字符 ''
data.loc[0, '成立年份'] = '\n' # 插入换行符 \n
data # 输出查看 data
df.iana()
检测以上数据中的缺失值,它可以让缺失值显示为 True,非缺失值显示为 False。data.isna() # 或者使用 data.isnull(),pd.isna(data)
NaN
和 None
都被 Pandas 检测为缺失值,因为NaN
是 Numpy 模块的空值类型,表示为 np.nan
,是 Not a Number 的简写,而 Pandas 是由 Numpy 开发而来的,所以保留了 NaN
,它在 Python 中是 float
类型(即小数型)数据;而 None 是 Python 中的空值对象,所以两者都会被判断为缺失值。但是 data
中的空字符,换行符却没有被判断为缺失值。这是因为他们都是字符型数据,Pandas 并不认为这些值是缺失值。但在实际的数据中,这些值又没有任何作用,可以被认为是缺失值。那我们怎么把这些值检测出来呢?pd.isna()
可以判断一个值是否为空值或者判断一个 DataFrame 或 Series 中的值是否是缺失值,缺失值显示为 True
,非缺失值显示为 False
。下面我们分别使用 pd.isna()
和正则表达式来识别不同类型缺失值。# 使用 pd.isna() 判断常规缺失值 NaN(np.nan)和 None
pd.isna(np.nan) # 返回 True, NaN 被判断为缺失值
pd.isna(None) # 返回 True, None 被判断为缺失值
pd.isna('') # 返回 False,空字符不被判断为缺失值
pd.isna('\n') # 返回 False,换行符不被判断为缺失值
pd.isna('\t') # 返回 False,制表符不被判断为缺失值
# 使用正则匹配字符型缺失值(空值,无意义值)
# 有返回值,说明被判断为缺失值
re.search('^\s+$|(^$)', '').group() #返回 ''
re.search('^\s+$|(^$)', '\n').group() #返回 '\n'
re.search('^\s+$|(^$)', '\t').group() #返回 '\t'
# 无返回值,所以含非空数据的空字符不会被误判为缺失值
re.search('^\s+$|(^$)', 'ab cd\nef\tg') # 无返回值
NaN
,然后就可以统一处理浮点型缺失值 NaN
,也可以将 NaN
替换为 字符型缺失值空字符 ''
。这样这些就可以参与到字符数据的运算中。3使用 Pandas 处理缺失值
(1)删除法处理缺失值
df.dropna()
,他可以删除含有缺失值的行或列;可以删除全为缺失值的行或列;也可以删除缺失值数量大于某个阈值的行或列。删除行需要指定参数 axis=0
,删除列则指定参数axis=1
;删除含有缺失值的数据需要指定参数how='any'
,删除全为缺失值的数据则需要指定参数how='all'
。下面将不再多举例说明。# 没有指定参数 inplace=True,所以该操作不会在原数据上生效,
# 而是返回一个处理后的新数据
data.dropna(how='any', axis=0)注意,这里由于空字符 ''
和 换行符等字符型缺失值不会被 Pandas 判断为缺失值,所以含有这些数据的行得以保留。
场景 2:删除缺失值数量大于 3 的所有列
df.dropna()
中的参数thresh
可以指定非缺失值的数量(正整数),表示非缺失值的数量,当非缺失值数量小于这个整数值,这一行/列(需要根据 axis 参数来指定)会被删除。这个描述可能比较绕,我们通过这个使用场景来感受一下。# 数据共有 15 行,缺失值数量大于 3 就等价于非缺失值数量不小于 12
data.dropna(axis=1, thresh=12)删除前: 删除后: 可以看出,由于 “资产总计(万元)” 含有 4 个缺失值,故此列被删除。需要注意的是,当已经指定了 how
参数时,thresh
参数将不再生效,起作用的将会是how
参数。需要注意的是, df.dropna()
默认不会修改原始的数据,而是返回一个经过处理的新数据,所以即使刚刚删除了几行或者几列,数据data
都没有发生变化。如果想要修改原数据,使删除操作在原始数据上生效,有以下两种方法。# 第一种:设置该方法的 inplace 参数为 True
data.dropna(inplace=True)
# 第二种:将新生成的数据赋值给原始数据的变量名
data = data.dropna()两种方法有着一样的效果,但是不能同时使用,否则不仅不能达到期待的效果,反而会误删数据。
(2)填充法处理缺失值
① 使用df.fillna()
df.fillna()
,他可以将缺失值替换为指定数据,也可以替换为缺失值附近的数据。替换为指定数据时,只需要传入一个要被替换为的数据即可;如果需要使用缺失值所在位置的前一个非缺失值来填充,只需要传入一个参数 method='ffill'
即可;使用后面一个非缺失值来填充时则需要传入参数 method='bfill'
。fillna()
方法既可以在整个数据上应用,也可以指定数据区域应用,最常见使用场景的是填充某一列的缺失值。场景 3:将数据中 "资产总计(万元)" 一列中的缺失值填充为 0
data['资产总计(万元)'].fillna(0)
这个结果是不是让人有一点小意外,虽然缺失值被填充为整数 0,但是填充后却是浮点型数值 0.0 ,而且返回的数据不是 DataFrame 类型,而是 Serise 类型。首先,整数 0 变为 浮点数 0.0 是因为字段 “资产总计(万元)” 的数据类型是 float 类型,其精度高于整数类型,当一列中的数据都是数值型时,Pandas 会自动将精度较低的数值类型转为精度更高的数值类型,即 float 型。其次,为什么只返回了一列数据,而且不是跟原始数据一样的 DataFrame 类型呢?因为 df.fillna()
方法与刚才介绍的df.dropna()
方法一样,也和 Pandas 中绝大多数涉及到数据修改的方法一样,他们都不会直接修改原始的数据,而是返回一个新的数据。这就是为什么会返回填充后的数据,而返回 Serise 类型的原因是,调用fillna()
方法的数据data['资产总计(万元)']
本身就是一个 Series ,所以会返回一个同样类型的数据。同样地,如果想要使修改数据的操作在原始数据上生效,有以下两种方法。 # 第一种:设置该方法的 inplace 参数为 True
data['资产总计(万元)'].fillna(0, inplace=True)
# 第二种:将新生成的数据赋值给原始数据的变量名
data['资产总计(万元)'] = data['资产总计(万元)'].fillna(0)
场景 4:填充字段“资产总计(万元)”中的缺失值,要求是使用该字段中上一个非缺失值填充后面的缺失值
# 使用缺失值所在位置的前一个非缺失值来填充,需要传入参数 method='ffill'
data['资产总计(万元)'].fillna(method='ffill')可以看到,该字段中所有缺失值都被前一个非缺失值填充。
② 使用df.replace()
df.fillna()
只能识别和填充 NaN 和 None,对于字符型缺失值却束手无策。这个时候 Pandas 中数值替换方法 df.replace()
就可以很好地解决这个问题,该方法可以将 DataFrame 中几乎所有数据值(除 None 外)替换你想要的值, df.replace()
方法有许多种使用方法,这里只为大家介绍一种最常用,最简单易懂的使用方式。就像 Excel 中的查找替换功能一样,只需要先后传入替换前的值和替换后的值即可。场景 5:将数据中所有的“采矿业”替换为“采矿产业”
# 将数据中的所有的“采矿业”替换为“采矿产业”
data.replace('采矿业', '采矿产业')有人可能会提问,将“矿业”替换为“矿产业”不是也能达到一样的效果吗?这样做在 Excel 中确实可行,但在 df.replace()
并不支持这种操作,因为该方法查找和替换的是数据值,而非数据值的某一部分。
df.replace()
是可以填充一些特定的数据值了,可是字符型缺失值有很多种形式,这样一个一个查找、替换是不是效率太低了?请看下面这个例子。场景 6:将数据中所有缺失数据替换为“数据缺失”
df.fillna()
填充缺失值。# 填充缺失值 NaN 和 None, 设置参数 inplace=True,使操作生效
data.fillna('数据缺失', inplace=True)
# 由于设置了参数使替换生效,故不会有任何返回值,这里重新输出 data
data
df.replace()
可以一次性将这些字符型缺失值处理掉。其原理是什么呢?很简单,df.replace()
方法是支持使用正则表达式的,我们将能够匹配所有字符型缺失值的正则表达式当作替换前的数据传入该方法,再设置参数 regex=True
,表示使用正则模式。就可以一次性替换字符型缺失值了。操作如下:# 设置参数 regex=True ,表示使用正则模式
# 设置参数 inplace=True,使操作生效
# '^\s+$|(^$)' 是一个能够匹配所有字符型缺失值的正则表达式
data.replace('^\s+$|(^$)', '数据缺失', regex=True, inplace=True)
data
df.fillna()
适合处理缺失值 NaN 和 None;而 df.replace()
的使用场景可以替换几乎所有值,尤其是字符型数据值。但是不能处理空值 None,所以这两个方法在缺失值填充方面是互补的。灵活使用他们,就可以填充或者修改所有缺失值。Part3重复值
score
。1如何检测重复值
df.duplicated()
。该方法有两个主要的参数用来设置检测重复值的条件。用法为:# 该方法返回一个 Series,重复的行会被标记为 True, 非重复行会被标记为 False
df.duplicated(subset=None, keep='first')
subset
表示判断是否重复所需的列,例如当检测数据 score
的重复值时,传入参数 subset=['学号','姓名']
,就表示使用数据的 “学号” 和 “姓名” 这两列来查重,这两列分别一样的数据行会被标记为重复数据。例如下图中红色矩形框内的三行数据。subset
参数的默认值是 None,使用默认值时,所有字段都会被用于查重,仅当所有字段都分别相同的数据行才会被认定是重复数据,例如数据 score 的最后两行。keep
用来确定如何标记重复值,可选的参数值及其含义如下:'first' :默认的参数值,将除了第一次出现的重复值标记为 True,即首次出现的重复值不会被认定为重复值。 'last' :将除了最后一次出现的重复值标记为 True,即最后一次出现的重复值不会被认定为重复值。 False :将所有重复值标记为 True。
下面用一个例子来体会一下 df.duplicated()
。
场景 7:根据“学号”和“姓名”字段找到并取出所有的重复行。
# 根据要求,指定查重字段为 '学号' 和 '姓名',所以参数 subset=['学号', '姓名']
# 根据要求,需要找到所有的重复值,所以参数 keep=False
score.duplicated(subset=['学号', '姓名'], keep=False)这个结果缺失与数据中的重复情况是一致的,但这样还不够。我们只是找到了重复数据并打上了 True
标签,却并没有把数据取出来,其实取出打上True
标签的数据只需要简单的一步就可以了。# 把查重结果作为条件筛选的条件,使用数据筛选的方法就可以取出数据了
score[score.duplicated(subset=['学号', '姓名'], keep=False)]
2删除重复值
df.drop_duplicates()
是专门用来删除重复值的方法,这个方法的原理就是根据 df.duplicated()
方法找出重复值后将它们删除。所以前者的主要参数和用法与后者是一样的。删除后返回一个新数据,不会直接修改原始数据。如果想要使删除操作在原始数据上生效,需要指定参数 inplace=True
,相信大家对这个参数的使用已经比较熟悉了。场景 8:对数据 score 做去重操作,重复的行数据只保留第一行
# 根据要求,我们只需要使用默认的参数值就可以了
# 指定参数 inplace=True,使操作在原始数据上生效
score.drop_duplicates(inplace=True)
score数据 score 中第4,5两行(行索引为 3 和 4)是完全一样的,参数 keep 的默认值是 'first',表示保留重复数据中首次出现的一行,所以第四行(行索引为 3)得以保留,第五行(索引为 4)被删除。
Part4总结
我们将在数据治理板块中推出一系列原创推文,帮助读者搭建一个完整的社科研究数据治理软硬件体系。该板块将涉及以下几个模块:
1. 计算机基础知识
2. 编程基础
(1) 数据治理 | 带你学Python之 环境搭建与基础数据类型介绍篇
(4) 数据治理 | 还在用Excel做数据分析呢?SQL它不香吗
(5) 数据治理 | 普通社科人如何学习SQL?一篇文章给您说明白
3. 数据采集
4. 数据存储
(1) 安装篇:数据治理 | 遇到海量数据stata卡死怎么办?这一数据处理利器要掌握
(2) 管理篇: 数据治理 | 多人协同处理数据担心不安全?学会这一招,轻松管理你的数据团队
(3) 数据导入:数据治理 | “把大象装进冰箱的第二步”:海量微观数据如何“塞进”数据库?
5. 数据清洗
(3) 数据治理 | 数据分析与清洗工具:Pandas数据选取与修改
(4) 数据治理 | 数据分析与清洗工具:用Pandas快速选出你的“心之所向”
(5)本期内容:数据治理 | 数据分析与清洗工具:Pandas缺失值与重复值处理
6. 数据实验室搭建
数据Seminar
这里是大数据、分析技术与学术研究的三叉路口
文 | 《大数据时代社科研究数据治理实务手册》
欢迎扫描👇二维码添加关注