数据分析中缺失值处理
缺失值(NA)是一个非常常见的问题,特别是在大样本的研究中。与其删除整个被试的数据,不如试试插值的方法。本文简单介绍了几种缺失值处理的方法。
数据-Boston Housing
人为引入缺失值,这样就可以比较各种插值方法的结果和实际原始数据的差别。
data ("BostonHousing", package="mlbench") # initialize the data # load the data
original <- BostonHousing # backup original data
# Introduce missing values
set.seed(100)
BostonHousing[sample(1:nrow(BostonHousing), 40), "rad"] <- NA
BostonHousing[sample(1:nrow(BostonHousing), 40), "ptratio"] <- NA
head(BostonHousing)
#> crim zn indus chas nox rm age dis rad tax ptratio b lstat medv
#> 1 0.00632 18 2.31 0 0.538 6.575 65.2 4.0900 1 296 15.3 396.90 4.98 24.0
#> 2 0.02731 0 7.07 0 0.469 6.421 78.9 4.9671 2 242 17.8 396.90 9.14 21.6
#> 3 0.02729 0 7.07 0 0.469 7.185 61.1 4.9671 2 242 17.8 392.83 4.03 34.7
#> 4 0.03237 0 2.18 0 0.458 6.998 45.8 6.0622 3 222 18.7 394.63 2.94 33.4
#> 5 0.06905 0 2.18 0 0.458 7.147 54.2 6.0622 3 222 18.7 396.90 5.33 36.2
#> 6 0.02985 0 2.18 0 0.458 6.430 58.7 6.0622 3 222 18.7 394.12 5.21 28.7
使用mice的md.pattern可以快速获得缺失值的统计
library(mice)
md.pattern(BostonHousing) # pattern or missing values in data.
#> crim zn indus chas nox rm age dis tax b lstat medv rad ptratio
#> 431 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0
#> 35 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1
#> 35 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1
#> 5 1 1 1 1 1 1 1 1 1 1 1 1 0 0 2
#> 0 0 0 0 0 0 0 0 0 0 0 0 40 40 80
处理方法
1.删除样本
删除或在模型构建时不包括缺失值,例如通过设置na.action = na.omit)包含缺失值的样本(行)。
2.删除变量
如果某个变量包含过多缺失值并且删除该变量可以保存更多的样本,可以考虑删除该变量。
3.用均值/中位数/众数插值
最简单的方法,容易操作而且结果还不错。
library(Hmisc)
impute(BostonHousing$ptratio, mean) # replace with mean
impute(BostonHousing$ptratio, median) # median
impute(BostonHousing$ptratio, 20) # replace specific number
# or if you want to impute manually
BostonHousing$ptratio[is.na(BostonHousing$ptratio)] <- mean(BostonHousing$ptratio, na.rm = T) # not run
library(DMwR)
actuals <- original$ptratio[is.na(BostonHousing$ptratio)]
predicteds <- rep(mean(BostonHousing$ptratio, na.rm=T), length(actuals))
regr.eval(actuals, predicteds)
#> mae mse rmse mape
#> 1.62324034 4.19306071 2.04769644 0.09545664
4.模型插值
4.1. kNN插值
DMwR :: knnImputation使用k-Nearest Neighbours方法来插补缺失值。对于要插值的每个观测值,它基于欧氏距离确定“ k”个最接近的观测值,并计算这些“ k”个观测值的加权平均值(基于距离加权)。简单的理解就是找到最近的几个邻居,使用它们的数据进行插值。
优点:一次插值所有缺失值
缺点:无法处理factor类型的变量
注意: 计算kNN的时候不能包括响应变量(y)
library(DMwR)
knnOutput <- knnImputation(BostonHousing[, !names(BostonHousing) %in% "medv"]) # perform knn imputation.
anyNA(knnOutput)
#> FALSE
actuals <- original$ptratio[is.na(BostonHousing$ptratio)]
predicteds <- knnOutput[is.na(BostonHousing$ptratio), "ptratio"]
regr.eval(actuals, predicteds)
#> mae mse rmse mape
#> 1.00188715 1.97910183 1.40680554 0.05859526
相较于均值插补,平均绝对百分比误差(mape)提高了约39%
4.2 rpart
rpart和mice都可以处理factor的变量,在调用rpart()时设置method = class。对于numeric,使用method = anova。
注意: 同样的不能在不能在响应变量(例子中的medv)上训练rpart。
library(rpart)
class_mod <- rpart(rad ~ . - medv, data=BostonHousing[!is.na(BostonHousing$rad), ], method="class", na.action=na.omit) # since rad is a factor
anova_mod <- rpart(ptratio ~ . - medv, data=BostonHousing[!is.na(BostonHousing$ptratio), ], method="anova", na.action=na.omit) # since ptratio is numeric.
rad_pred <- predict(class_mod, BostonHousing[is.na(BostonHousing$rad), ])
ptratio_pred <- predict(anova_mod, BostonHousing[is.na(BostonHousing$ptratio), ])
actuals <- original$ptratio[is.na(BostonHousing$ptratio)]
predicteds <- ptratio_pred
regr.eval(actuals, predicteds)
#> mae mse rmse mape
#> 0.71061673 0.99693845 0.99846805 0.04099908
与knImputation相比,平均绝对百分比误差(mape)另外提高了约30%。
actuals <- original$rad[is.na(BostonHousing$rad)]
predicteds <- as.numeric(colnames(rad_pred)[apply(rad_pred, 1, which.max)])
mean(actuals != predicteds) # compute misclass error.
#> 0.25
这会产生25%的误分类误差。
4.3. mice
Multivariate Imputation by Chained Equations
首先使用mice()构建模型,并使用complete()生成完成的数据。类似于sklearn里的fit + transform的操作。mice(df)函数可产生多组数据每组数据有对缺失数据的不同估算。complete()函数返回一个或多个这些数据集,默认为第一个。
library(mice)
miceMod <- mice(BostonHousing[, !names(BostonHousing) %in% "medv"], method="rf") # perform mice imputation, based on random forests.
miceOutput <- complete(miceMod) # generate the completed data.
anyNA(miceOutput)
#> FALSE
actuals <- original$ptratio[is.na(BostonHousing$ptratio)]
predicteds <- miceOutput[is.na(BostonHousing$ptratio), "ptratio"]
regr.eval(actuals, predicteds)
#> mae mse rmse mape
#> 0.36500000 0.78100000 0.88374204 0.02121326
与rpart相比,平均绝对百分比误差(mape)另外提高了〜48%
actuals <- original$rad[is.na(BostonHousing$rad)]
predicteds <- miceOutput[is.na(BostonHousing$rad), "rad"]
mean(actuals != predicteds) # compute misclass error.
#> 0.15
错误分类错误减少到15%,与rpart的25%相比,是一个很大的进步。
来源
http://r-statistics.co/Missing-Value-Treatment-With-R.html