一、認識缺失值
在我們的數據分析過程中,經常會碰到缺失值的情況。缺失值產生的原因很多,比如人工輸入失誤,系統出錯,或者是正常情況,比如未婚狀態下的子女個數肯定是0或者直接不填,這種情況就是正常的。所以我們處理缺失值的步驟一般是:
1) 識別缺失值
2) 檢查導致缺失值的原因
3) 刪除包含缺失值的行或列或者用合理的數值填補缺失值或者不處理
R使用 NA (不可得)代表缺失值, NaN (不是一個數)代表不可能值。
缺失的數據有三種類型:
將數據集中不含缺失值的變量(屬性)稱為完全變量,數據集中含有缺失值的變量稱為不完全變量,Little 和 Rubin定義了以下三種不同的數據缺失機制:
1)完全隨機缺失(Missing Completely at Random,MCAR)。數據的缺失與不完全變量以及完全變量都是無關的。
2)隨機缺失(Missing at Random,MAR)。數據的缺失僅僅依賴于完全變量。
3)非隨機、不可忽略缺失(Not Missing at Random,NMAR,or nonignorable)。不完全變量中數據的缺失依賴于不完全變量本身,這種缺失是不可忽略的。
以下圖中是R中用來處理缺失值的一些總結
二、識別缺失值
library(VIM)
a <- head(sleep)
is.na(a)
complete.cases(a) #查看行是否完整
nrow(na.omit(sleep)) #查看行沒有缺失值的行數
nrow(sleep)-nrow(na.omit(sleep)) #查看缺失值的行數
nrow(sleep[!complete.cases(sleep),]) #查看缺失值的行數
nrow(sleep[!complete.cases(sleep),])/nrow(sleep) #查看缺失值的占比
nrow(sleep[!complete.cases(sleep$Dream),])/nrow(sleep) #查看Dream這一列缺失值的占比
sum(!complete.cases(sleep$Dream))/nrow(sleep)
注意:complete.cases函數僅將 NA 和 NaN 識別為缺失值,無窮值( Inf 和 -Inf )被當作有效值。
以上代碼都是針對全部特征或者全部樣本而言,如果我們想查看某幾列或者某幾行的缺失情況,上面的代碼就有點麻煩。我們可以通過構建一個函數,并用向量化的方式來操作:
apply(mice::nhanes2,2,function(x){paste0((sum(is.na(x))/length(x))*100,"%")})
apply(mice::nhanes2,1,function(x){paste0((sum(is.na(x))/length(x))*100,"%")})
一般來說,缺失值可靠的最大閾值是數據集總數的5%。如果某些特征或樣本缺失的數據超過了5%,你可能需要忽略掉這些特征或樣本。
以上代碼都很簡單,但是不怎么形象,我們可以借助mice,VIM包中的一些函數對缺失值進行可視化探索,然后在決定下一步該怎么操作
base::summary函數
> summary(mice::nhanes2)
age bmi hyp chl
20-39:12 Min. :20.40 no :13 Min. :113.0
40-59: 7 1st Qu.:22.65 yes : 4 1st Qu.:185.0
60-99: 6 Median :26.75 NA's: 8 Median :187.0
Mean :26.56 Mean :191.4
3rd Qu.:28.93 3rd Qu.:212.0
Max. :35.30 Max. :284.0
NA's :9 NA's :10
三、探索缺失值
3.1列表顯示
mice::md.pattern函數
mice::md.pattern(sleep)
得到如下結果:
1代表不是缺失值,0代表缺失值
紅色部分,比如42,代表數據集sleep中每個變量的值都不是缺失值的行數有42行;
2代表在Span變量這里有缺失值,其他變量沒有缺失值的情況有兩行
黃色部分代表缺失值的個數
3.2圖形探究缺失值
VIM::aggr()
VIM::aggr(VIM::sleep,prop=F,numbers=T,col=c("darkblue","red"),sortVars=TRUE,
labels=names(VIM::sleep),ylab=c("Histogram of missing data", "Pattern"))
VIM::aggr(sleep,prop=T,numbers=T)
VIM::aggr(sleep,prop=F,numbers=F) #numbers操縱數值標簽
VIM::matrixplot()
VIM::marginplot( )
VIM::marginplot(VIM::sleep[,4:5])
左邊的紅色箱線圖代表Dream值缺失的情況下Sleep的分布,藍色箱線圖代表Dream值不缺失的情況下Sleep分布。底下的兩個箱線圖的關系反過來理解。左邊的(y軸)9個紅點代表缺失了Dream的Sleep值。兩個變量均有缺失值的觀測個數在最左下角那里,即2.可以看出Sleep和Dream變量大致呈正相關關系
如果對數據缺失假定為MCAR(完全隨機)類型正確的話,那么我們預期的紅色箱線圖和藍色箱線圖應該是非常相似的。
VIM 包有許多圖形可以幫助你理解缺失數據在數據集中的模式,包括用散點圖、箱線圖、直方圖、散點圖矩陣、平行坐標圖、軸須圖和氣泡圖來展示缺失值的信息,因此這個包很值得探索
3.3相關性探索缺失值
我們可以用指示變量代替數據集中的數據(1表示缺失值,0表示完整)生成影子矩陣
- 1)求影子矩陣中指示變量之間的相關性,看看哪些變量經常一起缺失
> data(sleep,package = "VIM")
>
> x<- as.data.frame(abs(is.na(sleep)))
> head(x)
BodyWgt BrainWgt NonD Dream Sleep Span Gest Pred Exp Danger
1 0 0 1 1 0 0 0 0 0 0
2 0 0 0 0 0 0 0 0 0 0
3 0 0 1 1 0 0 0 0 0 0
4 0 0 1 1 0 1 0 0 0 0
5 0 0 0 0 0 0 0 0 0 0
6 0 0 0 0 0 0 0 0 0 0
然后求相關性
> y <- x[which(apply(x,2,sum)>0)] #篩選出有缺失值的變量
> cor(y)
NonD Dream Sleep Span Gest
NonD 1.00000000 0.90711474 0.48626454 0.01519577 -0.14182716
Dream 0.90711474 1.00000000 0.20370138 0.03752394 -0.12865350
Sleep 0.48626454 0.20370138 1.00000000 -0.06896552 -0.06896552
Span 0.01519577 0.03752394 -0.06896552 1.00000000 0.19827586
Gest -0.14182716 -0.12865350 -0.06896552 0.19827586 1.00000000
通過以上相關性矩陣的結果可以看出Dream和NonD常常一起缺失(r=0.91)。可能性較小的是 Sleep 和 NonD 一起缺失(r=0.49),以及 Sleep 和 Dream (r=0.20)。
- 2)計算指示變量和原始變量的相關性,查看缺失變量和其他變量的相關性
> cor(sleep,y,use="pairwise.complete.obs")
NonD Dream Sleep Span Gest
BodyWgt 0.22682614 0.22259108 0.001684992 -0.05831706 -0.05396818
BrainWgt 0.17945923 0.16321105 0.007859438 -0.07921370 -0.07332961
NonD NA NA NA -0.04314514 -0.04553485
Dream -0.18895206 NA -0.188952059 0.11699247 0.22774685
Sleep -0.08023157 -0.08023157 NA 0.09638044 0.03976464
Span 0.08336361 0.05981377 0.005238852 NA -0.06527277
Gest 0.20239201 0.05140232 0.159701523 -0.17495305 NA
Pred 0.04758438 -0.06834378 0.202462711 0.02313860 -0.20101655
Exp 0.24546836 0.12740768 0.260772984 -0.19291879 -0.19291879
Danger 0.06528387 -0.06724755 0.208883617 -0.06666498 -0.20443928
Warning message:
In cor(sleep, y, use = "pairwise.complete.obs") : 標準差為零
可以忽略矩陣中的警告信息和 NA 值,這些都是方法中人為因素所導致的。
在該矩陣中第一列,可以看出BodyWgt,Gest,Exp和NonD的缺失相關性較多,其三個變量的值越大,NonD越有可能缺失。其他列可以相似得出結果。
表中的相關系數并不特別大,表明數據是MCAR的可能性比較小,更可能為MAR。但是也有可能是非隨機缺失。當缺乏強力的外部證據時,我們通常假設數據是MCAR或者MAR。
四、理解缺失數據的來由和影響
我們識別,探索的目的是要搞清楚:
- 缺失數據的比例多大?
- 缺失數據是否集中在少數幾個變量上,抑或廣泛存在?
- 缺失是隨機產生的嗎?
- 缺失數據間的相關性或與可觀測數據間的相關性,是否可以表明產生缺失值的機制?
對于以上問題的解答,將會影響我們如何處理缺失值并且使用什么統計方法進行操作。
如果缺失數據集中在幾個相對不太重要的變量上,那么你可以刪除這些變量(列刪除),然后再進行正常的數據分析。如果有一小部分數據(如小于10%)隨機分布在整個數據集中(MCAR),那么你可以分析數據完整的實例(行刪除),這樣仍可以得到可靠且有效的結果。如果可以假定數據是MCAR或者MAR,那么你可以應用多重插補法來獲得有效的結論。如果數據是NMAR,你則需要借助專門的方法,收集新數據
五、刪除缺失值(行刪除、列刪除)
-
簡單刪除法用的比較多
直接刪除含有缺失值的樣本時最簡單的方法,尤其是這些樣本所占的比例非常小時,用這種方法就比較合理,但當缺失值樣本比例較大時,這種缺失值處理方法誤差就比較大了。在采用刪除法剔除缺失值樣本時,我們通常首先檢查樣本總體中缺失值的個數
1
六、插補法
6.1推理法
推理法是基于數據間的數學關系或者邏輯關系來進行判斷填補缺失值的。
比如在 sleep 數據集中,變量 Sleep 是 Dream 和 NonD 變量的和。如果知道了其中的兩個變量,那么另外一個缺失的變量就能夠找出來。
又比如如果在一份問卷調查中有一個問題是是否是主管級以上職位以及直接下屬的個數。如果職位那是空的,但是卻填了下屬的個數是大于1的,那么可以推斷其是主管級以上。
6.2多重插補
在某些情況下,一些快速修復如均值替代或許是不錯的辦法。對于這種簡單的辦法,經常會給數據帶來偏差。例如,均值代替法對數據的平均值不會產生變化(這是我們所希望的)。但會減小數據的方差,這不是我們所希望的。
我們可以使用mice包來進行鏈式方程的多元插補
注意:在用mice時,一定要先把目標變量去除
利用mice包進行多元插補的步驟
- 1)首先使用mice函數將一個包含缺失值的數據集通過Gibbs抽樣來進行預測插補。每個包含缺失值的變量默認可通過數據集中其他變量預測得來。對于每個變量我們可以選擇預測模型的形式(基本插補法)和待選入的變量。預測的均值用來替換連續型變量中的缺失數據,而Logistic或多元Logistic回歸則分別用來替換二值目標變量(兩水平因子)或多值變量(多于兩水平的因子)。其他基本插補法包括貝葉斯線性回歸、判別分析、兩水平正態插補和從觀測值中隨機抽樣。
該過程不斷迭代直到所有缺失值收斂為止。這個過程會產生多個(默認5個)被插補好的完整數據集,因為插補有隨機的成分,所以這些完整數據集不一樣 - 2)然后使用with函數依次對完整數據集應用統計模型
- 3) 最后使用pool函數將不同完整數據集的統計結果合并成一組結果
最終模型的標準誤和p值都將準確地反映出由于缺失值和多重插補而產生的不
確定性。
下面我們通過實例來演示一下怎么用
6.2.1插補缺失值
library(mice)
data <- nhanes2
data[sample(25,5),1] <- NA
VIM::aggr(data)
imp <- mice(data,m=5,meth="pmm",seed = 1234)
summary(imp)
Multiply imputed data set
Call:
mice(data = data, m = 5, method = "pmm", seed = 1234)
Number of multiple imputations: 5
Missing cells per column:
age bmi hyp chl
5 9 8 10
Imputation methods:
age bmi hyp chl
"pmm" "pmm" "pmm" "pmm"
VisitSequence:
age bmi hyp chl
1 2 3 4
PredictorMatrix:
age bmi hyp chl
age 0 1 1 1
bmi 1 0 1 1
hyp 1 1 0 1
chl 1 1 1 0
Random generator seed value: 1234
參數注解: 1. m=5指的是插補數據集的數量,5是默認值 2. meth='pmm'指的是插補方法。在這里,我們使用預測均值匹配(Predictive mean matching )作為插補方法。其他插補方法可以通過methods(mice)來查看。
Built-in univariate imputation methods are:
pmm any Predictive mean matching
midastouch any Weighted predictive mean matching
sample any Random sample from observed values
cart any Classification and regression trees
rf any Random forest imputations
mean numeric Unconditional mean imputation
norm numeric Bayesian linear regression
norm.nob numeric Linear regression ignoring model error
norm.boot numeric Linear regression using bootstrap
norm.predict numeric Linear regression, predicted values
quadratic numeric Imputation of quadratic terms
ri numeric Random indicator for nonignorable data
logreg binary Logistic regression
logreg.boot binary Logistic regression with bootstrap
polr ordered Proportional odds model
polyreg unordered Polytomous logistic regression
lda unordered Linear discriminant analysis
2l.norm numeric Level-1 normal heteroskedastic
2l.lmer numeric Level-1 normal homoscedastic, lmer
2l.pan numeric Level-1 normal homoscedastic, pan
2lonly.mean numeric Level-2 class mean
2lonly.norm numeric Level-2 class normal
2lonly.pmm any Level-2 class predictive mean matching
mice函數中有一個參數defaultMethod,解釋為:
包含數字列的默認插補方法,包含2個級別的因子列和包含多于兩個級別的(無序或有序)因子的列的三個字符串的向量。 如果沒有指定,則使用以下默認值:pmm,預測平均匹配(數值數據)logreg,邏輯回歸插補(二進制數據,2級因子)polyreg,無序分類數據的多重回歸插補(因子> = 2級 )polr,比例賠率模型(有序,> = 2級)
從輸出結果看,5個完整數據集同時被創建,預測均值(pmm)匹配法被用來處理每個缺失數據的變量。VisitSequence展示了被插補的變量,可以看到age有5個被插補,bmi9個被插補,hyp8個被插補,chl10個被插補。預測變量矩陣( PredictorMatrix )展示了進行插補過程的含有缺失數據的變量,它們利用了數據集中其他變量的信息。(在矩陣中,行代表插補變量,列代表為插補提供信息的變量,1和0分別表示使用和未使用。)
如果想看插補的數據,可以:
#通過提取 imp 對象的子成分,可以觀測到實際的插補值
imp$imp$age
1 2 3 4 5
1 20-39 20-39 60-99 40-59 60-99
2 20-39 40-59 60-99 20-39 60-99
8 20-39 20-39 20-39 20-39 20-39
19 20-39 20-39 60-99 40-59 20-39
23 20-39 20-39 20-39 40-59 20-39
現在我們可以使用complete()函數來查看完整的插補數據
> complete(imp,action=1)
age bmi hyp chl
1 20-39 22.7 no 238
2 20-39 22.7 no 187
3 20-39 30.1 no 187
4 60-99 21.7 no 204
5 20-39 20.4 no 113
6 60-99 20.4 yes 184
7 20-39 22.5 no 118
8 20-39 30.1 no 187
9 40-59 22.0 no 238
10 40-59 30.1 yes 204
11 20-39 22.7 no 131
12 40-59 26.3 yes 184
13 60-99 21.7 no 206
14 40-59 28.7 yes 204
15 20-39 29.6 no 199
16 20-39 28.7 no 187
17 60-99 27.2 yes 284
18 40-59 26.3 yes 199
19 20-39 35.3 no 218
20 60-99 25.5 yes 186
21 20-39 26.3 no 238
22 20-39 33.2 no 229
23 20-39 27.5 no 131
24 60-99 24.9 no 186
25 40-59 27.4 no 186
參數action代表是剛才產生的5個插補數據集中的第幾個
查看初始數據和插補數據的分布情況
library(lattice)
xyplot(imp,age~bmi+hyp+chl,pch=18,cex=1)
我們希望看到的是洋紅點呈現出的形狀(插補值)跟藍色點(觀測值)呈現出的形狀是匹配的。從圖中可以看到,插補的值的確是“近似于實際值”。
另一個有用的圖是密度圖:
densityplot(imp)
同樣,我們希望看到的是洋紅色的5條線(即創建出來的5個完整數據集)和藍色線分布是相似的。洋紅線是每個插補數據集的數據密度曲線,藍色是觀測值數據的密度曲線
另一個有用的可視化是由stripplot()函數得到的包含個別點的變量分布圖
stripplot(imp,pch=20,cex=1.2)
6.2.2合并
假設我們下一步的分析是對數據擬合一個線性模型。我們或許會問應該選擇哪個插補數據集。mice包可以輕易的對每個數據集分別擬合一個模型,再把結果合并到一起。
fit <- with(imp,{
lm(bmi~age+hyp+chl)
})
pooled <- pool(fit)
summary(pooled)
est se t df Pr(>|t|) lo 95 hi 95 nmis fmi
(Intercept) 19.05591158 3.74955112 5.082185 15.883792 0.000113411 11.1024894 27.00933380 NA 0.1923327
age2 -3.65636382 3.05764709 -1.195810 4.375714 0.292523636 -11.8658629 4.55313522 NA 0.7103051
age3 -6.24533303 3.35922085 -1.859161 3.686536 0.142588045 -15.8932679 3.40260183 NA 0.7628207
hyp2 1.36228610 2.89278764 0.470925 5.737591 0.655062802 -5.7953290 8.51990118 NA 0.6181460
chl 0.05135278 0.02148144 2.390565 14.236578 0.031168511 0.0053514 0.09735415 10 0.2497429
lambda
(Intercept) 0.09665898
age2 0.60252591
age3 0.66160344
hyp2 0.50479575
chl 0.15126190
fit變量包含所有插補數據集的擬合結果,pool()函數將結果合并到一起。可以看到4個預測變量都不統計顯著
請注意,這里除了lm()模型給出的結果外還包含其它列:fmi指的是各個變量缺失信息的比例,lambda指的是每個變量對缺失數據的貢獻大小
6.3通過探索案例之間的相似性來填補缺失值
案例指的就是數據框中的行。比如說有兩行數據是相似的,然后其中一行的某些變量值是缺失的,那么該缺失值很有可能與另外一行的值是相似的。那么問題來了,相似性該怎么定義呢?在很多方法中,利用歐氏距離來度量相似性是比較常見的。這個距離可以非正式的定義為兩個案例的觀測值之差的平方和。
那當我們找到相似性的案例的時候,我們用什么值來代替缺失值呢?
第一種方法是簡單的計算我們所指定的n個相似性案例的中位數來插補數值型數據,用n個案例的眾數來插補缺失值。第二種方法是使用這些相似數據的加權均值。權重的大小隨著距離待插補缺失值的個案增大而減小。用高斯核函數從距離獲得權重。如果相似個案與待插補缺失值的個案的距離為d,則權重為:
在DMwR2包中,有個knnImputation函數,他利用一個變種歐氏距離來找到k個近鄰,這種歐氏距離可以同時應用于名義變量和數值變量。
6.4均值插補
均值這一概念嚴格來講應該是中心趨勢指標,有算術平均值,幾何平均值,結尾平均值,中位數,眾數等。我們這里改插補什么值應該看變量的分布。如果變量是呈正態分布或者接近于正態分布,那么所有觀測值都較好地聚集在平均值周圍,因此平均值就就是填補該類變量缺失值的最佳選擇。但是,如果變量是偏態的或者有離群點,平均值就不是代表中心趨勢的最佳指標,這時候中位數反而是最好的代替者。同時,其實數據呈正態分布時,中位數和均值是相等的。所以對于數值型數據來說,我們可以用中位數來代替。而對于離散型數據來講,眾數是一個很好的選擇。
- 在DMwR2包中有個centralImputation函數可以實現這個功能,但是不能進行分層插補,所以這里自己寫了一個可以進行分層插補的函數
我們可以將這種方法創建一個函數,以后就可以反復調用了:
- 功能:能夠進行分組插補缺失值(數值型為中位數,分類型為眾數)
Calculate <- function(x,df=NULL,wt=NULL){
#####################################
#df:數據框
#wt:字符串(df中的分類變量名稱)
#####################################
if(is.numeric(x)){
if(is.null(wt)){
median(x,na.rm = TRUE)
}else{
aggregate(x,by=list(df[,wt]),function(a) median(a,na.rm = TRUE))
}
}else{
x <- as.factor(x)
if(is.null(wt)){
levels(x)[which.max(table(x))]
}else{
aggregate(x,by=list(df[,wt]),function(a) levels(a)[which.max(table(a))])
}
}
}
FillMissingValue <- function(data,by=NULL){
####################################
#data:數據框
#by:字符串(分類變量名,用于分組插補缺失值)
###################################
if(is.null(by)){
for(i in seq(ncol(data))){
index <- which(is.na(data[,i]))
data[index,i] <- Calculate(data[,i])
}
}else{
for(i in seq(ncol(data))){
for(j in levels(data[,by])){
index <- which(is.na(data[,i]) & data[,by]==j)
a <- Calculate(data[,i],df=data,wt=by)
data[index,i] <- a[which(a[,1]==j),2]
}
}
}
return(data)
}
我們來測試一下:
b <- mice::nhanes2
VIM::aggr(b,prop=F,numbers=T)
- 不分組插補
b<-FillMissingValue(b)
VIM::aggr(b,prop=F,numbers=T)
- 以age為分組變量插補缺失值
b <- mice::nhanes2
test <- FillMissingValue(b,by="age")
VIM::aggr(test,prop=F,numbers=T)
可以對比下兩次的插補,值是不一樣的
可以將這兩個函數保存為一個R文件,要用的時候調用就行了
6.5極大似然估計
6.6 隨機森林插補
z <- missForest::missForest(data)
z$ximp
age bmi hyp chl
1 20-39 29.60491 no 182.6688
2 60-99 22.70000 no 187.0000
3 20-39 29.57666 no 187.0000
4 60-99 25.42770 yes 234.1083
5 20-39 20.40000 no 113.0000
6 60-99 24.29740 no 184.0000
7 20-39 22.50000 no 118.0000
8 20-39 30.10000 no 187.0000
9 40-59 22.00000 no 238.0000
10 40-59 27.42942 yes 216.9633
11 20-39 29.60491 no 182.6688
12 40-59 27.42942 yes 216.9633
13 60-99 21.70000 no 206.0000
14 40-59 28.70000 yes 204.0000
15 20-39 29.60000 no 182.6688
16 20-39 29.60491 no 182.6688
17 60-99 27.20000 yes 284.0000
18 40-59 26.30000 yes 199.0000
19 20-39 35.30000 no 218.0000
20 60-99 25.50000 yes 234.1083
21 20-39 29.60491 no 182.6688
22 20-39 33.20000 no 229.0000
23 20-39 27.50000 no 131.0000
24 60-99 24.90000 no 188.4600
25 40-59 27.40000 no 186.0000
還有很多缺失值的處理方法,以后用到再學吧
其他方法可以參考:http://blog.csdn.net/dminer/article/details/2504091
還有mice包的多元插補可以參考:https://www.jstatsoft.org/article/view/v045i03