0. 数据清洗
数据清洗是一项复杂的工作,该过程目的在于提高数据的质量,将脏数据清理干净,使数据具有完整性、唯一性、权威性、合法性、一致性等特点。Pandas常见的数据清洗操作有空值和缺失值的处理、重复值的处理、异常值的处理、统一数据格式等。
0.1 空值和缺失值的处理
空值一般表示数据未知、不适用或将在以后添加数据。缺失值是指数据集中某个或某些属性不完整主要原因有机械原因和人为原因,机械原因往往因为机器故障而人为原因可能由于人为隐瞒或录入失误而引起的。空值使用None
表示,缺失值使用NaN
表示,处理空值和缺失值通常有如下四个函数:
函数名 | 函数效果 |
---|---|
isnull() |
检查空值或缺失值 |
notnull() |
检查空值或缺失值 |
dropna() |
删除含有空值或缺失值的行或列 |
fillna() |
填充空值或缺失值 |
0.1.1 isnull()
和notnull()
isnull()
和notnull()
参数都是只有一个,表示检查空值对象,不同之处在于isnull()
如果检测到None
或NaN
则标记为True
而notnull()
检测到None
或NaN
则标记为False
,例如:
1 | import pandas as pd |
输出:
1 | A B C D |
0.1.2 dropna()
该方法作用是删除含有空值或缺失值的行或列。
语法:
1 | pandas.dropna(axis=0,how='any',thresh=None,subset=None,inplace=False) |
axis
:确定过滤行的行或列,默认为0
。取值为0
或index
时删除包含缺失值或空值的行;取值为1
或columns
时删除包含缺失值或空值的列。how
:确定过滤的标准,默认为any
,取值为any
时,则存在NaN
或None
则删除该行或该列;取值为all
时,则所有值都为NaN
或None
是才删除该行或该列。thresh
:表示有效数据的最小要求,如果传入了2,则是要求该行或该列至少有两个非NaN
或None
时将其保留。subset
:表示在特定子集中的寻找NaN
或None
inplace
:表示是否在原数据上操作默认为False
,如果设为True
则在原数据操作,如果设为False
则修改原数据的副本返回新的数据。
实例:
1 | import pandas as pd |
输出为:
1 | A B C D |
0.1.3 fillna()
该方法可实现空值或缺失值的填充。
语法:
1 | pandas.fillna(value=None,methon=None,axis=None,inplace=False,limit=None,downcast=None,**kwargs) |
value
:用于填充的数值methon
:表示填充方式,默认为None
另外支持一下取值pad/ffill
:将最后一个有效数据向后传播,也就是说用缺失值前面的一个值代替缺失值。backfill/bfill
:将最后一个有效值向前传播,也就是说用缺失值后面的一个值代替缺失值。
limit
:可以连续填充的最大数据量,默认为None
注意:
method
和value
参数不能同时使用。
实例:
1 | import pandas as pd |
输出为:
1 | 用常数6填充缺失值: |
0.2 重复值的处理
当数据中出现了重复值,大多数情况需要删除,例如下表中:
id | name | height | sex | |
---|---|---|---|---|
0 | 1 | zz | 160 | 男 |
1 | 2 | yxy | 160 | 男 |
2 | 3 | zxy | 170 | 女 |
3 | 4 | wcc | 170 | 男 |
4 | 4 | wcc | 170 | 男 |
显然索引为3
和4
的数据完全相同需要进行删除,对于重复值的处理,Pandas提供了以下两个函数:
函数 | 函数效果 |
---|---|
duplicated() |
标记是否有重复值 |
drop_duplicates() |
删除重复值 |
0.2.1 duplicated()
重复值标记函数
语法:
1 | duplicated(subset=None,keep='first') |
subset
:用于识别重复的列标签或列标签序列,默认识别所有列标签。keep
:删除重复值并保留第一次出现的项,取值如下:first
:从前向后查找,除了第一次出现的项其余相同的均标记为重复。(默认值)last
:从后往前查找,除了最后一次出现的项其余相同的均标记为重复。False
:所有相同的标记均标记为重复。
实例:
1 | import pandas as pd |
输出为:
1 | 0 1 2 3 4 |
0.2.2 drop_duplicates()
重复值删除函数
语法:
1 | drop_duplicates(subset=None,keep="first",inplace=False) |
实例:
1 | import pandas as pd |
输出为:
1 | id name height sex |
0.3 异常值的处理
异常值指的是样本中的个别值,其数值明显偏离它所属样本的其余观测值:
如图,蓝色圈出来的点明显高于其他值。对于异常值的检测通常使用3σ原则(拉依达准则)
或箱型图
检测。
0.3.1 基于3σ原则
检测异常值
3σ原则又称拉依达原则,它是指假设一组检测数据中只含有随机误差,对其进行计算处理得到标准偏差,按一定概率确定一个区间,凡是超过这个区间的误差都是粗大误差,在此误差范围内的数据应予以剔除,在正态分布概率公式中,σ
表示标准差,μ
表示平均数,f(x)
表示正态分布函数,表达式如下:
其中正态函数的分布函数图如下所示:
- 数值分布在(
μ-σ
,μ+σ
)中的概率为68.27% - 数值分布在(
μ-2σ
,μ+2σ
)中的概率为95.44% - 数值分布在(
μ-3σ
,μ+3σ
)中的概率为99.70%
通过3σ
原则,可以定义如下函数:
1 | import numpy as np |
对于一组数据例如:1,2,3,4,5,1000,2,3,4,5,3,8,23,12,66
,通过3σ原则
来进行检测:
1 | import pandas as pd |
检测结果为:
1 | 5 1000 |
0.3.2 基于箱型图检测异常值
箱型图是一种用作显示一组数据分散情况的统计图,在箱型图中,异常值被定义为小于Ql-1.5IQR或大于Qu+1.5IQR的值。其中:
- Ql称为下四分位数,表示全部观察值中有四分之一的数据取值比他小
- Qu称为上四分位数,表示全部观察值中有四分之一的数据取值比他大
- IQR称为四分位数的间距,是上四分位数Qu与下四分位数Ql之差,其间包含了全部观察值的一半。
离散点表示的是异常值,上界表示除异常值以外数据中最大值;下界表示除异常值以外数据中最小值。
箱型图根据实际的数据进行绘制,对数据没有任何要求,箱型图判断异常值的标准是以四分位数和四分位距为基础。在Pandas中提供了一个boxplot()
方法用于绘制箱型图:
1 | import pandas as pd |
使用
vscode
或pycharm
进行可视化需要使用绘图库,如果需要直接显示可以通过使用Jupyter
进行快速编辑,安装教程如下:通过在
Jupyter
中输入:
1
2
3
4
5
6
7
8 import pandas as pd
data = pd.DataFrame({
'A': [1, 2, 3, 4],
'B': [2, 3, 5, 2],
'C': [1, 4, 7, 4],
'D': [1, 5, 30, 3]
})
data.boxplot(column=['A', 'B', 'C', 'D'])即可达到实时显示的目的:
可以看出根据检出结果异常值被检测出,对于异常值,通常会使用以下四种方式处理异常值:
- 删除异常值记录
- 使用具体值进行替换,例如使用前后两个观测值的平均值进行修正
- 不处理,直接在具有异常值的数据集中统计分析
- 视为缺失值,利用缺失值的处理方法修正异常值
如果需要对异常值进行替换,可以使用replace()
函数。
replace()
函数语法:
1 | replace(to_replace=None,value=None,inplace=False,limit=None,regex=False,method='pad') |
to_replace
:表示查找被替换的方式value
:用来替换任何匹配to_replace
的值,默认为None
limit
:表示前向或后向填充的最大尺寸间隙regex
:接收布尔值或与to_replace
相同的类型,默认为False
,表示是否将to_replace
和value
解释为正则表达式。
实例:
1 | import pandas as pd |
通过replace
方法将data
中的异常值30
直接在原数据集中替换为3
,注意,replace()
方法中的to_replace
参数的传入值也可为列表list
输出:
1 | A B C D |
0.4 更改数据类型
在处理数据时可能会遇到数据类型不一致的问题,对于数据类型的统一有如下三种方式:
- 创建时指定数据类型
astype()
方法numberic()
方法
0.4.1 创建时指定数据类型
使用dtype
参数,例如:
1 | import pandas as pd |
可以看出输入的对象是字符型,但是通过指定dtype
最终的对象为int
型。
1 | A int32 |
0.4.2 astype()
方法
语法:
1 | astype(dtype,copy=True,errors='raise',** kwargs) |
dtype
:数据类型copy
:是否建立副本,默认为True
errors
:错误采取的处理方式,默认为raise
(允许引发异常),还可以设置为ignore
(抑制异常)
实例:
1 | import pandas as pd |
输出:
1 | A float64 |
0.4.3 to_numeric()
astype()
方法存在局限性,通过to_numeric()
函数可以将传入参数转换为数值类型。
语法:
1 | pandas.to_numeric(arg,error='raise',downcast=None) |
arg
:表示要转换的数据,可以是list
,tuple
,Series
errors
:错误采取的处理方式
to_numeric
函数无法直接操作DataFrame
对象
实例:
1 | import pandas as pd |
输出:
1 | float64 |
1. 数据合并
1.1 轴向堆叠数据
通过使用concat()
函数可以沿着一条轴对多个对象进行堆叠,语法格式为:
1 | pandas.concat(objs,axis=0,join='outer',join_axes=None,ignore_index=False,keys=None,levels=None,names=None,verify_integrity=False,sort=None,copy=True) |
axis
:表示连接的轴向,默认为0,还可以设置为1join
:表示连接方式,inner
表示内连接,outer
表示外连接,默认外连接ignore_index
:接收布尔值,默认为False
,如果设置为True
,则表示清除现有索引并重置索引值keys
:接收序列,表示添加最外层索引levels
:用于构建MultiIndex
的特定级别(唯一)names
:在设置了keys
和level
参数后,用于创建分层级别的名称verify_integerity
:检查新的连接轴是否包含重复值。接收布尔值,当设置为True
时,如果有重复的轴将会抛出错误,默认为False
1.1.1 横向堆叠、纵向堆叠、内连接、外连接
横向堆叠以行
为标准,相同名称的行保存为一行,而纵向堆叠以列
为标准,相同名称的列保存为一列;内连接是将两个DataFrame
对象中都有的列或行进行堆叠连接,而外连接是将两个DataFrame
对象所有的列和行进行堆叠连接。
横向堆叠默认不考虑列索引而纵向堆叠默认不考虑行索引,缺失值使用
NaN
填充
实例:
1 | import pandas as pd |
输出:
1 | A B C D |
1.2 主键合并数据
主键合并类似于关系型数据库,根据一个或多个键将不同的DataFrame
对象连接起来,大多数是将两个DataFrame
对象中重叠的列作为合并的键,Pandas中提供了用于主键合并的merge()
函数,语法为:
1 | pandas.merge(left,right,how='inner',on=None,left_on=None,right_on=None,left_index=False,right_index=False,sort=False,suffixes=('_x','_y'),copy=True,indicator=False,validate=None) |
left
:参与合并的左侧DataFrame
对象right
:参与合并的右侧DataFrame
对象how
:表示连接方式,默认为inner
,该参数还支持如下取值:left
:使用左侧的DataFrame
的键,类似SQL的左外连接right
:使用右侧的DataFrame
的键,类似SQL的右外连接outer
:使用两个DataFrame
所有的键,类似于SQL全连接inner
:使用两个DataFrame
键的交集,类似于SQL内连接
on
:用于连接的列名,必须存在于两个DataFrame
对象中left_on
:以左侧DataFrame
作为连接键right_on
:以右侧DataFrame
作为连接键left_index
:左侧的行索引用作连接键right_index
:右侧的行索引用作连接键sort
:是否排序,接收布尔值,默认为False
suffixes
:用于追加到重叠列名的末尾,默认为(_x,_y)
在使用
merge()
函数进行合并时默认使用重叠的列索引作为合并键,并采用内连接的方式合并数据。
1.2.1 通过主键合并
针对同一个主键存在两张不同字段的表,根据主键整合到一张表里面,例如上图中左侧两张表均有索引为key
的一列数据,两表通过key
列合并为一个表。即:
1 | import pandas as pd |
输出:
1 | key A B C D |
1.2.2 多个主键合并
通过merge()
函数可以对含有多个重叠列的DataFrame
对象进行合并,例如上图中使用key
和B
作为合并键得到右侧合并结果:
1 | import pandas as pd |
输出为:
1 | key A B C D |
1.3 根据行索引合并数据
1.3.1 join()
方法
join()
方法能够通过索引或者指定的列来连接DataFrame
。
语法:
1 | join(other,on=None,how='left',lsuffix='',rsuffix='',sort=False) |
on
:用于连接的列名lsuffix
:接收字符串,用于在左侧重叠的列名后添加后缀rsuffix
:接收字符串,用于在右侧重叠的列名后添加后缀sort
:接收布尔值,根据连接键对合并的数据进行排序,默认为False
实例:
1 | import pandas as pd |
输出:
1 | A B C D |
1.4 合并重叠数据
在处理数据时,当一个DataFrame
对象出现了缺失值,对于这些缺失数据如果想要通过其他的DataFrame
对象中的数据填充,可以通过combine_first()
方法填充缺失数据。
语法:
1 | combine_first(other) |
other
:用于接收填充缺失值的对象
实例:
1 | import pandas as pd |
输出:
1 | A B key |
使用
combine_first
方法合并两个DataFrame
对象时,必须确保他们的行索引和列索引有重叠部分
2. 数据重塑
在Pandas
中大多数情况DataFrame
对象更便于操作,因为DataFrame
对象很容易获取每行或每列的数据。但是有时候需要将DataFrame
对象转换为Series
对象,因此需要对数据进行重塑。
2.1 重塑层次化索引
重塑层次化索引的操作主要是stack()
方法和unstack()
方法,前者是将数据的列旋转为行,后者是将数据的行旋转成列。
2.1.1 stack()
方法
语法:
1 | DataFrame.stack(level=-1,dropna=True) |
level
:表示操作的内层索引,若设置为0,表示操作外层索引,默认为-1dropna
:表示是否将旋转后的缺失值删除,默认为True
(过滤缺失值)
实例:
1 | import pandas as pd |
输出:
1 | 0 A A0 |
通过:
1 | print(type(result)) |
可查看结果为:
1 | <class 'pandas.core.series.Series'> |
2.1.2 unstack
方法
语法:
1 | DataFrame.unstack(level=-1,fill_value=None) |
fill_value
:若产生了缺失值,则可以设置这个参数用来替换NaN
实例:
将上例中还原:
1 | print(result.unstack()) |
输出:
1 | A B |
2.2 轴向旋转
2.2.1 pivot
方法
pivot
方法提供的功能是根据给定行索引或列索引重新组织一个DataFrame
对象。
语法:
1 | DataFrame.pivot(index=None,columns=None,values=None) |
index
:用于创建新DataFrame
对象的行索引,如果未设置,则使用原DataFrame
对象的索引。columns
:列索引values
:值
实例:
出售日期 | 商品名称 | 价格(元) |
---|---|---|
2017年5月25日 | 荣耀9青春版 | 999 |
2017年5月25日 | 小米6x | 1399 |
2017年5月25日 | OPPO A1 | 1399 |
2017年6月18日 | 荣耀9青春版 | 800 |
2017年6月18日 | 小米6x | 1200 |
2017年6月18日 | OPPO A1 | 1250 |
将上述表格转化为下表:
商品名称 出售日期 |
荣耀9青春版 | 小米6x | OPPO A1 |
---|---|---|---|
2017年5月25日 | 999 | 1399 | 1399 |
2017年6月18日 | 800 | 1200 | 1250 |
1 | import pandas as pd |
3. 数据转换
当数据进行清洗之后,需要对数据进行一些合理的转换,使这些数据更符合分析的要求。
3.1 重命名轴索引
重命名轴索引是数据分析中比较常见的操作,可以通过rename()
方法重命名个别列索引或行索引的标签或名称。
3.1.1 语法格式:
1 | rename(mapper=None,index=None,columns=None,axis=None,copy=True,inplace=False,level=None) |
index,columns
:表示待转换的行索引和列索引axis
:表示轴的名称,可以使用index
或columns
,也可以使用数字0或1copy
:表示是否复制底层的数据,默认为False
inplace
:默认为False
,表示是否返回新的Pandas
对象,如果设置为True
,则会忽略复制的值level
:表示级别的名称,默认为None
。对于多级索引,只重命名指定的标签。
3.1.2 实例
将下列表格列索引更改为
a
,b
,c
A | B | C | |
---|---|---|---|
0 | 1 | 2 | 3 |
1 | 4 | 5 | 6 |
2 | 7 | 8 | 9 |
3 | 10 | 11 | 12 |
1 | import pandas as pd |
3.1.3 输出
1 | a b c |
3.1.4 其他说明
使用rename()
方法也可以对行索引进行重命名:
1 | result_s = result.rename(index={1: 'a', 2: 'b'}) |
输出:
1 | a b c |
3.2 离散化连续数据
有时候我们需要将数据进行离散化,可以使用cut()
函数。
3.2.1 函数语法:
1 | pandas.cut(x,bins,right=True,labels=None,retbins=False,precision=3,include_lowest=False,duplicates='raise') |
x
:表示要分箱的数组,必须是一维的bins
:接收int和序列类型的数据right
:是否包含右端点,默认包含labels
:用于生成区间的标签retbins
:是否返回binprecision
:精度,默认保留三位小数include_lowest
:是否包含左端点
3.2.2 实例
1 | import pandas as pd |
3.2.3 输出
1 | [(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (35, 60], (25, 35], (60, 100], (35, 60], (25, 35]] |
3.3 哑变量处理类别型数据
在Pandas中,可以使用get_dummies()
函数对类别特征进行哑变量处理。语法如下:
1 | pandas.get_dummies(data,prefix=None,prefix_sep='_',dummy_na=False,columns=None,sparse=False,drop_first=False,dtype=None) |
data
:可接收数组,DataFrame
或Serise
对象,表示哑变量处理的数据prefix
:表示列名的前缀,默认为Noneprefix_sep
:用于附加前缀作为分隔符,默认为_
dummy_na
:表示是否为NaN值添加一列,默认为Falsesparse
:表示虚拟列是否是稀疏的,默认为Falsedrop_first
:是否通过从k个分类级别中删除第一个级来获得k-1个分类级别,默认为False
例如:
1 | import pandas as pd |
输出为:
1 | col__司机 col__学生 col__导游 col__工人 col__教师 |