深入浅出R语言数据分析
上QQ阅读APP看书,第一时间看更新

3.1 缺失值的识别与处理

缺失值是在进行数据分析任务时较常遇见的问题之一。产生缺失值的原因很多,如数据在存储的过程中出现了错误,或者在获取数据的时候没有获取到数据,从而产生了空值。

处理缺失值问题的第一步是识别数据中的缺失值,包括哪些字段有缺失值,不同字段缺失值的比例是多少。然后需要对缺失值进行处理,处理方式有很多种,包括:直接删除包含缺失值的行,这种处理方法的前提是缺失值的占比不能太高;对缺失值进行填补,填补方式也有很多种,如用均值进行填补、用插值的方式进行填补,还可以用回归、聚类、决策树等方法进行填补。

R提供了很多处理缺失值的工具,下列这些包都可以用于解决缺失值问题。

(1)VIM:提供了一系列的缺失值的可视化方法及缺失值的填补方法。

(2)naniar:提供了更加高效的汇总与处理R缺失值的方法。

(3)missMDA:提供了多变量的缺失值处理方法。

(4)Amelia:提供了bootstrap+EM算法处理缺失值。

(5)mice:提供了多种对缺失值进行填补的方法,如回归树、随机森林、逻辑回归。

(6)missForest:提供了一种使用随机森林填补缺失值的方法。

这些包都可以用来处理缺失值。处理缺失值的基本思路是,首先需要了解数据中缺失值的情况,然后选择合适的方法对缺失值进行处理。

本节主要介绍VIM包,使用VIM包对数据的缺失值进行可视化,然后进行填补。用到的数据来自VIM包,叫作sleep数据集,这个数据集一共有262个观测值、9个特征,获取方式如下:

3.1.1 缺失值的识别与描述性统计

在R中,缺失值一般是以NA的形式出现的,并且可以通过is.na判断向量中是否包含缺失值:

这里生成了一个向量tmp,向量中的第三个元素是缺失值,然后通过is.na( )函数进行检验。从结果中可以发现,在is.na( )返回的结果中,第三个元素的值是TRUE,表明原数据中的第三个元素为NA。

同样可以使用is.na( )来检测数据框中是否存在缺失值,这个函数会对数据框中的所有元素进行判断:

如果想知道有多少行数据是完整的,可以使用complete.cases( )函数。这个函数会判断每一行是否存在缺失值,如果某一行存在缺失值,则其返回FALSE,否则返回TRUE。sleep数据集一共有262个观测值、9个特征,通过complete.cases( )函数可以查看有多少行的数据没有缺失值:

从结果中可以发现,有42行的数据是不包含缺失值的。

3.1.2 缺失值的可视化展示

对缺失值的可视化展示可以更好地帮助我们去了解数据的缺失情况,包括数据的整体缺失情况、数据中不同特征的缺失情况。可以使用VIM包的aggr( )函数对数据集进行可视化展示,如图3.1所示。

图3.1 数据缺失情况(1)

图3.1(a)展示了每一个特征的缺失值比例,可以看到,每一个特征的缺失值比例是不一样的。图3.1(b)不仅显示了数据中每一个特征的缺失值情况,而且显示了数据中缺失值的分布情况,可以看到,有一些特征的缺失值比较集中,如Sleep,有一些特征的缺失值比较分散,如NonD。

aggr( )函数返回了一个结果,返回的结果给出了每一个变量缺失值的具体情况。通过这样的方式,可以对整个数据集的缺失值有一个整体的了解。

对数据集整体的缺失值情况有一定的了解之后,需要了解不同变量之间的缺失值是否存在某些关系。假设有两个字段——age和gender,其中age存在缺失值。通过age和gender的缺失值分析,可能存在的情况是只有男性的age存在缺失值,女性的不存在。那么,这就是一个很有意思的结论,可以帮助我们进一步去了解缺失值产生的原因。

可以通过barMiss去分析某一个特征在另一个特征不同取值的情况下的缺失值情况,因此barMiss只能传递包含最多两个特征的数据集。下面的代码首先选取了sleep数据集中的Exp和Sleep两个特征,然后用barMiss分析Exp与Sleep这两个特征的关系。数据缺失情况如图3.2所示。

图3.2 数据缺失情况(2)

Exp一共有5个取值,即1到5。从图3.2中可以看到,Sleep特征的缺失值只存在于Exp取值为1和5时,特别是当Exp为5时,Sleep有比较多的缺失值。在实际项目中,通过这种方式,可以很方便地了解两个变量之间的缺失值情况,进一步分析产生缺失值的原因。

3.1.3 缺失值的处理方法

对数据集的缺失值情况有一定的了解之后,需要对数据集的缺失值进行处理,处理方式包括直接删除、用统计量(均值、中位数、众数)填补、用模型的方法填补、使用回归模型填补、使用KNN填补等。

1.直接删除缺失值

直接删除缺失值是最简便的一种处理缺失值的方式,可以直接删除包含缺失值的行或者包含缺失值的列。但并不是所有的情况都可以采用这种方式。直接删除缺失值需要满足以下条件。

● 缺失值的占比比较小。

● 删除缺失值之后的数据集依然是整体的一个充分抽样。

第一点的意思是,缺失值比较少,其删除不会影响特征的分布。第二点的意思是,缺失值在数据集中是分散的,不是聚集起来的。如果缺失值是聚集起来的,那么直接删除其会影响数据的分布。之前有一个例子提到两个字段——age和gender,通过age和gender的缺失值分析,可能存在的情况是只有男性的age存在缺失值,女性的不存在。这个时候如果直接删除缺失值,那么男性样本就会减少很多,则gender这个特征的分布就会产生变化。

当满足以上两个条件的时候,可以直接删除缺失值。另外,对于特征而言,如果某一个特征的缺失值占比太高(70%~80%),则也可以直接删除此特征。在R中,可以直接使用na.omit( )对数据集中的缺失值进行删除。

从结果中可以看出,数据集中的缺失值已经被删除了。

2.使用均值、中位数填补缺失值

另一种处理缺失值最直观的方式就是使用均值与中位数填补缺失值。使用均值和中位数填补缺失值是一种非常容易理解的方式。这种方式非常简单,并且在某些情况下填补效果也很不错。在有些情况使用均值填补缺失值可能不是很合适,例如,数据的范围非常大,这个时候就并不是很适合使用均值填补缺失值。

在R中可以直接用数据的均值、中位数直接替换掉缺失值;也可以使用naniar包中的impute_mean_all( )或者impute_median_all( )用于对数据集进行填补,直接将需要填补的数据集传入impute_mean_all( )或者impute_median_all( ),其返回结果就是处理完缺失值的数据集。下面的代码是对sleep数据集进行缺失值填补。

3.使用KNN填补缺失值

使用KNN填补缺失值的基本思想是,对于每一个存在缺失值的行,去寻找与这一行最相似的K行,然后取这K行数据的平均值或者加权平均值对缺失的数据进行填补。VIM包提供了KNN的缺失值的处理方法,KNN( )函数的使用方式很简单,只需要将需要处理的数据集传入KNN( )函数,返回的结果是使用K均值填补缺失值之后的数据集。

KNN算法的优点很多,其基于数据之间的相似性,使用相似的数据值填补缺失值,这样的做法比直接用均值或者中位数进行填补更加合理、准确。使用KNN填补缺失值,不仅可以对数值型的变量进行填补,而且可以对离散型数据进行填补。KNN算法的关键点之一是选择合适的距离计算方法,这里可以使用3种距离,即欧氏距离(euclidean)、曼哈顿距离(manhattan)和gower距离。

需要注意的是,欧氏距离和曼哈顿距离主要计算连续变量之间的距离,如果数据包含离散变量,或者既包含连续变量又包含离散变量,就需要使用gower距离。

4.使用回归模型填补缺失值

使用回归模型填补缺失值是基于数据之间的相关性的,如果数据之间没有关系,那么无法使用回归模型填补缺失值。利用回归模型填补缺失值首先要建立以缺失值为因变量的回归模型,构建好回归模型之后,利用有缺失值的数据的自变量进行预测,将预测的结果填补为缺失值。这里使用regressionImp( )进行缺失值的填补,第一个参数是回归模型的公式,左边是回归模型的因变量,右边是回归模型的自变量(通过“~”隔开),其中因变量是有缺失值需要处理的特征;第二个参数是需要处理的数据集。需要注意的是,与lm( )函数构建回归模型不同的是,regressionImp( )函数中模型公式的左边可以包含多个因变量,这时会对每个因变量建立回归模型填补缺失值。

使用regressionImp( )填补缺失值,函数的第一个参数是回归模型的参数,~左边是需要填补的特征,右边是用于建立回归模型的特征。如果数据中的缺失值比较多,则可以使用robust回归,能够有更好的效果,使用robust回归的方式是在函数中添加一个参数,即robust==TRUE。

5.使用随机森林模型填补缺失值

随机森林模型是集成机器学习模型,通过许多决策树共同构成。随机森林处理缺失值的原理是,首先给缺失值预先设定一下,连续变量用均值或者中位数预设,离散的特征使用众数预设;然后使用所有数据构建随机森林模型,这时会记录每一组数据在决策树中每一步的分类路径,进而判断哪一组数据与缺失值的分类路径最接近。通过这种方式构建了数据之间的相似度,然后根据相似度,对连续数据使用加权平均的方式填补缺失值,对离散变量使用加权投票的方式填补缺失值。

总而言之,其本质上也是寻找相似的数据,用相似的数据来填补缺失值。下面使用随机森林模型对缺失值进行填补,使用的是mice包中的mice( )函数,函数的第一个参数是需要处理的数据集,第二个参数method是缺失值填补的方法,如果指定其为rf,则表示使用随机森林进行缺失值填补。

这里指定了缺失值的填补的方法(method)为rf,意味着指定使用随机森林模型进行填补。mice包用于填补缺失值的方法还有很多,例如:

● ppm——预测均值匹配。

● midastouch——带权的预测均值匹配。

● sample——随机抽样填补。

● cart——使用分类树和回归树填补缺失值。

● mean——使用均值填补缺失值。

● norm——使用贝叶斯线性模型填补缺失值。

● norm.nob——使用忽略模型误差的回归模型填补缺失值。

● norm.boot bootstrap——使用回归模型填补缺失值。

● quadratic——使用二次项填补缺失值

● logreg——使用逻辑回归填补缺失值。

● logreg.boot——使用bootstrap逻辑回归填补缺失值。

● polyreg——使用多项式模型填补缺失值。

● lad——使用线性判别模型填补缺失值。

需要使用某方法,只需要在模型中改变相应的method即可。需要注意的一点是,有些缺失值的填补方法只能应用于连续变量,如mean;有些则只能应用于分类变量,如logreg。

6.基于降维的缺失值处理方法

在R中,使用降维的方法可以非常高效地对缺失数据进行处理,missMDA包可以对连续变量、离散变量和混合变量进行缺失值的处理。

对连续变量的数据处理的基本过程是,首先建立含有缺失值的PCA模型,确定好主成分的数量,然后通过构建好的主成分模型进行插值。

一般而言,使用多重对应分析(MCA)方法处理分类变量数据,使用混合数据因子分析(FAMD)方法处理混合变量的数据。首先进行主成分分析,使用estim_ncpPCA( )函数对数据进行主成分分析,获取最优的主成分个数。

这里通过带缺失值的主成分分析,可知需要选取5个主成分,然后进行插值:

这样,就完成了数据填补。到这里,本节介绍了缺失值的数据探索方法、可视化的方法及缺失值的填补方法。缺失值的处理是数据探索过程中的一个非常重要的环节,接下来介绍数据探索的其他部分。