当我们在分析一个数据集以及做计算的时候,通常没那么简单。缺失数据是个很重要的问题。
如果缺失的数据量相对数据集的大小来讲比较小并且为了不偏离分析,忽略少量的样本或许是最后的策略。然而,忽略一些数据点的同时也会丢失数据的一些信息量,并且这也取决于你所遇到的情况。你或许应该考虑在删除一些潜在有用的数据点之前对数据集做一些修复。
在某些情况下,一些快速修复如均值替代或许是不错的办法。对于这种简单的办法,经常会给数据带来偏差。例如,均值代替法对数据的平均值不会产生变化(这是我们所希望的)。但会减小数据的方差,这不是我们所希望的。
R中的mice
包通过合理的数据值可以帮助我们填充缺失值。这些合理的数据值都是从一个分布中得到的,这个分布是根据缺失数据点的特定情况设计的。
在本篇文章,我们使用R中的airquality
数据集来做数据填充。
为了达到这篇文章的目的,我会从数据集中删除一些数据点。
data <- airquality data[4:10,3] <- rep(NA,7) data[1:5,4] <- NA
对于分类变量而言,代替分类变量通常是不可取的。一些通用的做法是根据观察到的模式来代替缺失的分类变量。然而,这种做法是值得怀疑。在这个案例中没有缺失的分类变量数据,我们把它从数据集中移除了,通过summary()
来查看下数据。
data <- data[-c(5,6)] summary(data) Ozone Solar.R Wind Temp Min. : 1.00 Min. : 7.0 Min. : 1.700 Min. :57.00 1st Qu.: 18.00 1st Qu.:115.8 1st Qu.: 7.400 1st Qu.:73.00 Median : 31.50 Median :205.0 Median : 9.700 Median :79.00 Mean : 42.13 Mean :185.9 Mean : 9.806 Mean :78.28 3rd Qu.: 63.25 3rd Qu.:258.8 3rd Qu.:11.500 3rd Qu.:85.00 Max. :168.00 Max. :334.0 Max. :20.700 Max. :97.00 NA's :37 NA's :7 NA's :7 NA's :5
显然,Qzone变量的数据点缺失最多。下面我们要深入挖掘丢失数据的模式。
缺失的数据有两种类型:
MCAR:完全随机缺失,这是数据缺失的理想状况。
MNAR:非随机缺失,这是一个比较严重的问题。在这种情况下,你可能需要去检查数据的收集过程并且试着理解数据为什么会丢失。例如,大多数人在一项调查中不回答某个问题,为什么他们这样做呢?是问题不清楚吗?
假设数据缺失的类型是MCAR,过多的数据丢失也是一个问题。通常,一个可靠的最大阈值是数据集总数的5%。如果某些特征或样本缺失的数据超过了5%,你可能需要忽略掉这些特征或样本。因此,我们用一个简单的函数来检查下哪些特征(列)和样本(行)的数据缺失超过了5%。
pMiss <- function(x) {sum(is.na(x))/length(x)*100} apply(data,2,pMiss) apply(data,1,pMiss) Ozone Solar.R Wind Temp 24.183007 4.575163 4.575163 3.267974 [1] 25 25 25 50 100 50 25 25 25 50 25 0 0 0 0 0 0 0 0 0 0 [22] 0 0 0 25 25 50 0 0 0 0 25 25 25 25 25 25 0 25 0 0 25 [43] 25 0 25 25 0 0 0 0 0 25 25 25 25 25 25 25 25 25 25 0 0 [64] 0 25 0 0 0 0 0 0 25 0 0 25 0 0 0 0 0 0 0 25 25 [85] 0 0 0 0 0 0 0 0 0 0 0 25 25 25 0 0 0 25 25 0 0 [106] 0 25 0 0 0 0 0 0 0 25 0 0 0 25 0 0 0 0 0 0 0 [127] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
我们看到,Qzone列的数据点缺失大约25%。因此,我们可能会考虑从分析中剔除它或者是对它做更多的收集。其他变量都低于5%的阈值,我们可以保留它们。对于样本而言,只需缺少一个特征就会导致一个样本25%的数据缺失。当缺失2个或更多特征的时候应当尽可能的丢弃。
mice
包提供了一个很好的函数md.pattern()
,用它可以对缺失数据的模式有个更好的理解。
library(mice) md.pattern(data) Temp Solar.R Wind Ozone 104 1 1 1 1 0 34 1 1 1 0 1 4 1 0 1 1 1 3 1 1 0 1 1 3 0 1 1 1 1 1 1 0 1 0 2 1 1 1 0 0 2 1 1 0 0 1 2 1 0 1 0 1 2 1 0 0 0 0 4 5 7 7 37 56
输出结果显示,有104个样本是完整的,34个样本仅缺失Qzone观测值,4个样本样本仅缺失Solar.R值等等。
利用VIM
包可以帮助我们得到一个很好的视觉展现,如下所示:
library(VIM) aggr_plot <- aggr(data, col = c('navyblue', 'red'), numbers=TRUE, sortVars=TRUE, labels=names(data), cex.axis=.7, gap=3, ylab=c("Histogram of missing data", "Pattern"))
这幅图帮助我们了解到,将近70%的样本不缺失任何信息,22%的样本缺失Qzone值,其余的显示其他缺失模式。通过这种方法对缺失模式有更清晰的了解。
另一个很有帮助的可视化方法是绘制一个箱形图。
marginplot(data[c(1,2)])
显然,在这里我们被限定一次只能绘制两个变量。然而,我们也可以得到一些有趣的思考。
左边的红色箱线图展示的是在Qzone值缺失的情况下Solar.R的分布,而蓝色箱线图展示的Qzone值不缺失的情况下Solar.R的分布。同样的,Qzone箱线图在底部。
如果对数据缺失假定为MCAR类型正确的话,那么我们预期的红色箱线图和蓝色箱线图应该是非常相似的。
mice
函数通过链式方程生成多元插补,详见文档
tempData <- mice(data,m=5,maxit=50,meth='pmm',seed=500) summary(tempData) Multiply imputed data set Call: mice(data = data, m = 5, method = "pmm", maxit = 50, seed = 500) Number of multiple imputations: 5 Missing cells per column: Ozone Solar.R Wind Temp 37 7 7 5 Imputation methods: Ozone Solar.R Wind Temp "pmm" "pmm" "pmm" "pmm" VisitSequence: Ozone Solar.R Wind Temp 1 2 3 4 PredictorMatrix: Ozone Solar.R Wind Temp Ozone 0 1 1 1 Solar.R 1 0 1 1 Wind 1 1 0 1 Temp 1 1 1 0 Random generator seed value: 500
参数注解: 1. m=5
指的是插补数据集的数量,5是默认值 2. meth='pmm'
指的是插补方法。在这里,我们使用预测均值匹配(Predictive mean matching )作为插补方法。其他插补方法可以通过methods(mice)
来查看。
如果你想查看插补的数据,例如Qzone变量,输入下面的代码行即可。
tempData$imp$Ozone 1 2 3 4 5 5 13 20 28 12 9 10 7 16 28 14 20 25 8 14 14 1 8 26 9 19 32 8 37 ...
输出显示了每个插补数据集(顶部第一行)中每个观测值(左侧第一列)的插补数据。
如果需要查看每个变量所用的插补方法,mice
可以很容易的做到。
tempData$meth Ozone Solar.R Wind Temp "pmm" "pmm" "pmm" "pmm"
现在,我们可以使用complete()
函数返回完整的数据集。
completedData <- complete(tempData,1)
缺失的值被五个数据集的第一个数据集做了替换。如果希望使用另一个数据集,只需更改complete()
函数的第二个参数。
我们利用一些有用的图对初始数据和插补后的数据分布做对比
library(lattice) xyplot(tempData,Ozone ~ Wind+Temp+Solar.R,pch=18,cex=1)
我们希望看到的是洋红点呈现出的形状(插补值)跟蓝色点(观测值)呈现出的形状是匹配的。从图中可以看到,插补的值的确是“近似于实际值”。
另一个有用的图是密度图:
densityplot(tempData)
洋红线是每个插补数据集的数据密度曲线,蓝色是观测值数据的密度曲线。再次根据我们之前的假定,我们希望这些分布是相似的。
另一个有用的可视化是由stripplot()
函数得到的包含个别点的变量分布图。
stripplot(tempData, pch = 20, cex = 1.2)
假设我们下一步的分析是对数据拟合一个线性模型。你或许会问应该选择哪个插补数据集。mice
包可以轻易的对每个数据集分别拟合一个模型,再把结果合并到一起。
modelFit1 <- with(tempData,lm(Temp~ Ozone+Solar.R+Wind)) summary(pool(modelFit1)) est se t df Pr(>|t|) (Intercept) 72.812078768 2.95380500 24.650266 84.18464 0.000000e+00 Ozone 0.163094287 0.02607674 6.254397 57.78569 5.236295e-08 Solar.R 0.009679676 0.00789576 1.225933 37.48960 2.278691e-01 Wind -0.352582008 0.21639828 -1.629320 92.89136 1.066321e-01 lo 95 hi 95 nmis fmi lambda (Intercept) 66.938301817 78.68585572 NA 0.1477818 0.1277731 Ozone 0.110891894 0.21529668 37 0.2155848 0.1888975 Solar.R -0.006311604 0.02567095 7 0.3004189 0.2640672 Wind -0.782312735 0.07714872 0 0.1300747 0.1115442
modelFit1
变量包含所有插补数据集的拟合结果,pool()
函数将结果合并到一起。显然,仅从Qzone变量来看的话,是统计显著的。
请注意,这里除了lm()
模型给出的结果外还包含其它列:fim指的是各个变量缺失信息的比例,lambda指的是每个变量对缺失数据的贡献大小。有关更多信息,请参考本文末尾的引用文章。
记住,我们之前对mice
函数初始化了一个特定的seed,因此所得的结果多少依赖于我们最初的选择。为了减少这种影响,我们可以通过更改mice()
函数默认m=5
的参数来插补更多的数据集。如下所示:
tempData2 <- mice(data,m=50,seed=245435) modelFit2 <- with(tempData2,lm(Temp~ Ozone+Solar.R+Wind)) summary(pool(modelFit2)) est se t df Pr(>|t|) (Intercept) 73.156084276 2.803010282 26.099114 129.3154 0.000000e+00 Ozone 0.166242781 0.024926976 6.669192 118.4408 8.645631e-10 Solar.R 0.009046835 0.007374103 1.226839 114.5471 2.223989e-01 Wind -0.382700790 0.202976584 -1.885443 136.6735 6.149264e-02 lo 95 hi 95 nmis fmi lambda (Intercept) 67.610387851 78.70178070 NA 0.11141367 0.0977762 Ozone 0.116882484 0.21560308 37 0.16290744 0.1488906 Solar.R -0.005560458 0.02365413 7 0.18096774 0.1667911 Wind -0.784081566 0.01867999 0 0.07425875 0.0608104
在考虑初始化随机种子后,我们得到的结论跟之前基本是一致的――仅从Qzone变量来看是统计显著的。
这篇文章的完整代码可以在这里找到。
注:对多重插补感兴趣的小伙伴可以参考这篇文章mice:Multivariate Imputation by Chained Equations in R
本文由雪晴数据网负责翻译整理,原文请参考Imputing Missing Data with R; MICE package作者Michy Alice。转载请注明原文链接http://www.xueqing.cc/cms/article/98