R語言基礎(chǔ)8--R語言缺失值、異常值和重復(fù)值的識(shí)別與處理


R語言基礎(chǔ)系列:


一、缺失值

1. 缺失值的識(shí)別和處理

缺失值存在的影響:無法進(jìn)行求均值、求和等運(yùn)算。(一般這些函數(shù)中都會(huì)有na.rm參數(shù),設(shè)置成TRUE時(shí)可以自動(dòng)去除缺失值)

x <- c(1,2,3,NA,NA,4)
x
# [1]  1  2  3 NA NA  4
sum(x)
# [1] NA
sum(x,na.rm = T)
# [1] 10
1.1 is.na函數(shù)的簡(jiǎn)單使用:

判斷一個(gè)數(shù)據(jù)框中有沒有缺失值,有多少缺失值

#判斷有沒有缺失值
is.na(x)
# [1] FALSE FALSE FALSE  TRUE  TRUE FALSE
#判斷有多少缺失值(TRUE=1, FALSE=0)
sum(is.na(x))
# [1] 2
#感嘆號(hào)起到取非的作用
x[!is.na(x)]
# [1] 1 2 3 4
#如果沒有!就只返回NA
x[is.na(x)]
# [1] NA NA

以iris數(shù)據(jù)集為基礎(chǔ)生成一個(gè)新的含缺失值的數(shù)據(jù)集iris_na

#在iris數(shù)據(jù)集的1-4列(數(shù)值型變量),1到最后一行中隨機(jī)抽取五個(gè)值,用缺失值NA替換(i in 1:4是在1到4列中對(duì)i值做一個(gè)遍歷)
iris_na <- iris
for(i in 1:4){
  iris_na[sample(1:nrow(iris),5),i]=NA
}
1.2 使用sapply函數(shù)?which來找到這5個(gè)缺失值
# which就是一個(gè)指針函數(shù),意思是在哪里
sapply(iris_na[,1:4],function(x)which(is.na(x)))
#      Sepal.Length Sepal.Width Petal.Length Petal.Width
# [1,]           28          12           10          19
# [2,]           54          39           20          24
# [3,]           68          76           45          26
# [4,]          132         117          119          69
# [5,]          141         128          127         143
# 每一列的缺失值的行數(shù)被顯示出來 
1.3 使用psych包中的describe函數(shù)(describe函數(shù)可以極大程度的返回了數(shù)據(jù)集的基本統(tǒng)計(jì)值)??
describe(iris_na)
#              vars   n mean   sd median trimmed  mad min max range  skew kurtosis   se
# Sepal.Length    1 145 5.83 0.82    5.8    5.80 1.04 4.3 7.7   3.4  0.26    -0.68 0.07
# Sepal.Width     2 145 3.06 0.44    3.0    3.04 0.44 2.0 4.4   2.4  0.31     0.06 0.04
# Petal.Length    3 145 3.77 1.75    4.4    3.79 1.63 1.0 6.7   5.7 -0.32    -1.40 0.15
# Petal.Width     4 145 1.21 0.76    1.3    1.20 1.04 0.1 2.5   2.4 -0.12    -1.34 0.06
# Species*        5 150 2.00 0.82    2.0    2.00 1.48 1.0 3.0   2.0  0.00    -1.52 0.07
#前四列都只返回了145個(gè)值,說明存在5個(gè)缺失值
1.4 計(jì)算缺失值的比例
sapply(iris_na[,1:4],function(x)sum(is.na(x)/nrow(iris_na)))
# Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
#   0.03333333   0.03333333   0.03333333   0.03333333
# 缺失值的和/總行數(shù)
1.5 存在缺失值的回歸分析
lm(Sepal.Length~Sepal.Width,data = iris_na)

# Call:
# lm(formula = Sepal.Length ~ Sepal.Width, data = iris_na)

# Coefficients:
# (Intercept)  Sepal.Width  
#      6.4824      -0.2105 

lm(Sepal.Length~Sepal.Width,data = iris_na,na.action = na.omit)

# Call:
# lm(formula = Sepal.Length ~ Sepal.Width, data = iris_na, na.action = na.omit)

# Coefficients:
# (Intercept)  Sepal.Width  
#      6.4824      -0.2105 

na.action默認(rèn)是na.omit,因此在使用lm求回歸時(shí)寫不寫這個(gè)參數(shù)都可以。

2. 填補(bǔ)缺失值

??在進(jìn)行缺失值的填補(bǔ)的時(shí)候一定要緊密結(jié)合數(shù)據(jù)背景和專業(yè)背景,否則易出現(xiàn)矯枉過正,適得其反。

2.1 簡(jiǎn)單操作系列:使用均值填補(bǔ)缺失值
# 求均值
mean_value <- sapply(iris_na[,1:4],mean,na.rm=TRUE)
mean_value
# Sepal.Length  Sepal.Width Petal.Length 
#     5.844138     3.064138     3.785517 
#  Petal.Width 
#     1.201379 

for(i in 1:4){
  iris_na[is.na(iris_na[,i]),i]=mean_value[i]
}
summary(iris_na)
describe(iris_na)

is.na(iris_na[,i])是在iris_na的1到4列對(duì)進(jìn)行遍歷,判斷數(shù)據(jù)框中有沒有缺失值,返回的是TRUE或FALSE。iris_na[is.na(iris_na[,I],i]就相當(dāng)于根據(jù)is.na(iris_na[,i]), i的結(jié)果從iris_na中進(jìn)行提取

如下方式是使用均值對(duì)所有缺失值進(jìn)行填補(bǔ),在缺失值存在于多個(gè)不同變量中,如肝癌,肺癌,胰腺癌等患者的術(shù)后存活時(shí)間不適合用所有數(shù)值的均值來填補(bǔ),需要分類處理。

cancer <- data.frame(id=1:1000,sur_days=sample(100:1000,1000,replace = TRUE),
+                      type=sample(c('colon','liver','lung'),1000,replace = TRUE),
+                      treatment=sample(c('chemo','surg'),1000,replace = TRUE))
head(cancer)
#   id sur_days  type treatment
# 1  1      717  lung     chemo
# 2  2      699 liver     chemo
# 3  3      762 liver      surg
# 4  4      400 liver     chemo
# 5  5      599  lung     chemo
# 6  6      933  lung      surg

#生成缺失值
cancer[sample(1:1000,90),2] <- NA
mean_value <- tapply(cancer$sur_days,list(cancer$type,cancer$treatment),mean,na.rm=TRUE)
mean_value
#          chemo     surg
# colon 529.7751 550.5411
# liver 533.8811 563.5965
# lung  568.9627 517.8435

#缺失值填補(bǔ)
for(i in 1:3){
  for(j in 1:2){
    cancer$sur_days[is.na(cancer$sur_days)&cancer$type==rownames(mean_value)[i]
                    &cancer$treatment==colnames(mean_value)[j]]=mean_value[i,j]
  }
}

summary(cancer)
describe(cancer)
2.2 填補(bǔ)缺失值的高級(jí)操作

演示數(shù)據(jù)集準(zhǔn)備

mlbench包中的BostonHousing包
library(mlbench)
data("BostonHousing")
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
original_data <- BostonHousing

#隨機(jī)生成隨機(jī)數(shù)
set.seed(2021)
BostonHousing[sample(1:nrow(BostonHousing),80),'rad'] <- NA
BostonHousing[sample(1:nrow(BostonHousing),80),'ptratio'] <- NA

對(duì)缺失值進(jìn)行操作

  • 2.2.1 mice包??

mice包中的md.pattern函數(shù),可以反映數(shù)據(jù)集中缺失值情況

library(mice)
md.pattern(BostonHousing)
#     crim zn indus chas nox rm age dis tax b lstat medv rad ptratio    
# 359    1  1     1    1   1  1   1   1   1 1     1    1   1       1   0
# 67     1  1     1    1   1  1   1   1   1 1     1    1   1       0   1
# 67     1  1     1    1   1  1   1   1   1 1     1    1   0       1   1
# 13     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  80      80 160

0表示缺失,1表示不缺失。最下面一行是每一個(gè)變量中缺失值的總數(shù)。最下面一行顯示rad和ptratio各有80個(gè)缺失,共有160個(gè)缺失。左邊一列359表示359個(gè)觀測(cè)都沒有缺失,有67個(gè)觀測(cè)rad變量缺失,ptratio沒有缺失。有67個(gè)變量ptratio缺失,rad沒有缺失。有13個(gè)觀測(cè)red和ptratio都缺失。

對(duì)缺失值進(jìn)行插補(bǔ)

mice_mod <- mice(BostonHousing[,!names(BostonHousing)%in% 'medv'],method = 'rf')
# rf是隨機(jī)森林算法 
# BostonHousing[,!names(BostonHousing)%in% 'medv']是為了禁止medv這一列進(jìn)入隨機(jī)森林模型

#  iter imp variable
#   1   1  rad  ptratio
#   1   2  rad  ptratio
#   1   3  rad  ptratio
#   1   4  rad  ptratio
#   1   5  rad  ptratio
#   2   1  rad  ptratio
#   2   2  rad  ptratio
#   2   3  rad  ptratio
#   2   4  rad  ptratio
#   2   5  rad  ptratio
#   3   1  rad  ptratio
#   3   2  rad  ptratio
#   3   3  rad  ptratio
#   3   4  rad  ptratio
#   3   5  rad  ptratio
#   4   1  rad  ptratio
#   4   2  rad  ptratio
#   4   3  rad  ptratio
#   4   4  rad  ptratio
#   4   5  rad  ptratio
#   5   1  rad  ptratio
#   5   2  rad  ptratio
#   5   3  rad  ptratio
#   5   4  rad  ptratio
#   5   5  rad  ptratio

判斷是否還存在缺失值
mice包中的complete()函數(shù)用于接受前面生成的模型,并生成一個(gè)完整數(shù)據(jù)。

mice_output <- complete(mice_mod)
anyNA(mice_output)
[1] FALSE
# 已經(jīng)不存在缺失值了
head(mice_output)
#      crim zn indus chas   nox    rm  age    dis rad tax ptratio      b lstat
# 1 0.00632 18  2.31    0 0.538 6.575 65.2 4.0900   1 296    18.3 396.90  4.98
# 2 0.02731  0  7.07    0 0.469 6.421 78.9 4.9671   2 242    17.8 396.90  9.14
# 3 0.02729  0  7.07    0 0.469 7.185 61.1 4.9671   2 242    17.8 392.83  4.03
# 4 0.03237  0  2.18    0 0.458 6.998 45.8 6.0622   3 222    18.7 394.63  2.94
# 5 0.06905  0  2.18    0 0.458 7.147 54.2 6.0622   3 222    18.7 396.90  5.33
# 6 0.02985  0  2.18    0 0.458 6.430 58.7 6.0622   3 222    18.7 394.12  5.21

檢驗(yàn)替換后的值和原本數(shù)據(jù)框中的值的是否一樣,結(jié)果發(fā)現(xiàn),預(yù)測(cè)的值80%都和未被NA替換的值一樣,精度很高(高于均數(shù)、眾數(shù)、中位數(shù)等等)。

predict <- mice_output[is.na(BostonHousing$rad),'rad']
actuals <- original_data$rad[is.na(BostonHousing$rad)]
mean(actuals!=predict)
# 0.2
  • 2.2.2 Hmisc
    Hmisc包的impute()函數(shù)(較粗略,不推薦)
# 使用均值進(jìn)行插補(bǔ)
im_mean <- impute(BostonHousing$ptratio,mean) #除了mean也可以使用median等,或直接使用特定值
head(im_mean)
#         1         2         3         4         5         6 
# 18.49765*  17.80000  17.80000  18.70000  18.70000  18.70000 
# 帶星號(hào)表示是插補(bǔ)的值

#缺失值替換
BostonHousing$ptratio <- NULL
BostonHousing$im_mean <- im_mean
  • 2.2.3VIM包??
    使用本身就含有缺失值的airquality數(shù)據(jù)集進(jìn)行操作
data('airquality')
head(airquality)
#   Ozone Solar.R Wind Temp Month Day
# 1    41     190  7.4   67     5   1
# 2    36     118  8.0   72     5   2
# 3    12     149 12.6   74     5   3
# 4    18     313 11.5   62     5   4
# 5    NA      NA 14.3   56     5   5
# 6    28      NA 14.9   66     5   6
# 缺失值主要集中在1-2列

采用md_pattern進(jìn)行查看

md.pattern(airquality)
#     Wind Temp Month Day Solar.R Ozone   
# 111    1    1     1   1       1     1  0
# 35     1    1     1   1       1     0  1
# 5      1    1     1   1       0     1  1
# 2      1    1     1   1       0     0  2
#        0    0     0   0       7    37 44
#一共有44個(gè)缺失值

VIM的過人之處是可以使用aggr()函數(shù)對(duì)缺失值進(jìn)行可視化

library(VIM)
aggr_plot <- aggr(airquality,col=c('red','green'),numbers=TRUE,sortVars=TRUE,
                  labels=names(airquality),cex.axis=0.7,gap=3)
# color定義缺失值和非缺失值的顏色
# numbers=TRUE表示顯示確實(shí)值的比例
# sortVars=TRUE表示對(duì)變量按照缺失值的多少進(jìn)行排序
# labels=names(airquality)定義圖的標(biāo)題
# cex.axis=0.7,gap=3定義坐標(biāo)軸的大小和圖之間的間距

#  Variables sorted by number of missings: 
#  Variable      Count
#     Ozone 0.24183007
#   Solar.R 0.04575163
#      Wind 0.00000000
#      Temp 0.00000000
#     Month 0.00000000
#       Day 0.00000000
# col是設(shè)置非缺失值和缺失值顏的參數(shù)。
# numbers=TRUE是顯示缺失值和非缺失值的比例。
# sortVars=TRUE是根據(jù)缺失值的多少進(jìn)行排序。
# labels是對(duì)生成的圖進(jìn)行標(biāo)簽化。
# cex.axis是定義坐標(biāo)軸的大小。
綠色代表缺失,左圖中左邊兩個(gè)變量有缺失,右邊三個(gè)沒有缺失。柱子的長短顯示了缺失值的多少。右邊的圖反應(yīng)缺失值的模式。Ozone單獨(dú)缺失的比例是0.229,Solar.R單獨(dú)缺失的比例是0.033,兩個(gè)一起缺失的比例是0.013。

marginplot()是另一個(gè)對(duì)缺失值進(jìn)行可視化的函數(shù)

marginplot(airquality[1:2]) #對(duì)存在缺失值的1-2列進(jìn)行操作
藍(lán)色空心點(diǎn)表示沒有缺失,紅色實(shí)心點(diǎn)表示缺失。紫色(37個(gè))表示兩者都缺失。圖左邊的盒形圖,紅色表示Ozone缺失時(shí)Solar.R的分布,藍(lán)色表示不缺失時(shí)的分布,下面的盒形圖紅色表示Solar.R缺失時(shí)Ozone的分布,藍(lán)色表示Solar.R不缺失時(shí)Ozone的分布。

VIM 除了進(jìn)行可視化以外,還可以采取高級(jí)的方法如線性回歸、KNN等來對(duì)缺失值進(jìn)行插補(bǔ)。
如:使用線性回歸法進(jìn)行插補(bǔ)

# 使用R語言內(nèi)置sleep數(shù)據(jù)集進(jìn)行演示
sleepIm <- regressionImp(Sleep + Gest + Span + Dream + NonD ~ BodyWgt + BrainWgt,data = sleep)
# 使用BodyWgt和BrainWgt這兩個(gè)不存在缺省的變量來對(duì)Sleep、Gest、Span、Dream和NonD這五個(gè)存在缺省的參數(shù)進(jìn)行預(yù)測(cè)(線性回歸的方式)和插補(bǔ)

插補(bǔ)之后

head(sleepIm)
#    BodyWgt BrainWgt       NonD      Dream Sleep     Span Gest Pred Exp Danger Sleep_imp Gest_imp Span_imp Dream_imp NonD_imp
# 1 6654.000   5712.0 -11.732867 -0.6897314   3.3 38.60000  645    3   5      3     FALSE    FALSE    FALSE      TRUE     TRUE
# 2    1.000      6.6   6.300000  2.0000000   8.3  4.50000   42    3   1      3     FALSE    FALSE    FALSE     FALSE    FALSE
# 3    3.385     44.5   8.987353  2.0132372  12.5 14.00000   60    1   1      1     FALSE    FALSE    FALSE      TRUE     TRUE
# 4    0.920      5.7   9.017324  2.0148478  16.5 15.50179   25    5   2      3     FALSE    FALSE     TRUE      TRUE     TRUE
# 5 2547.000   4603.0   2.100000  1.8000000   3.9 69.00000  624    3   5      4     FALSE    FALSE    FALSE     FALSE    FALSE
# 6   10.550    179.5   9.100000  0.7000000   9.8 27.00000  180    4   4      4     FALSE    FALSE    FALSE     FALSE    FALSE

#上面一部分顯示原有缺失值已被插補(bǔ),下面一部分是判斷原有的值是否為缺省值,F(xiàn)ALSE表示不是缺省值,TRUE表示是缺省值,但已經(jīng)被填補(bǔ)。

regressionImp函數(shù)中還有個(gè)參數(shù)family,選'auto'。因?yàn)橐蜃兞堪ㄟB續(xù)型和離散型等,他們采取的回歸方式是不一樣的。auto模式可以自動(dòng)采取合適的方法。

二、異常值

生成一個(gè)數(shù)據(jù)集

set.seed(2017)
mmhg <- sample(60:250,1000,replace = TRUE)
range(mmhg)
# [1]  60 250
min(mmhg)
# [1] 60
max(mmhg)
# [1] 250
quantile(mmhg)
#    0%   25%   50%   75%  100% 
#  60.0 106.0 153.5 198.0 250.0 
fivenum(mmhg)
# [1]  60.0 106.0 153.5 198.0 250.0

處理異常值的自定義函數(shù)

在使用時(shí),下述代碼唯一需要修改的就是var_name>230這里。在該代碼中大于230被定義為極端值,可根據(jù)實(shí)際情況進(jìn)行修改。

outlierHH <- function(dt,var){
  var_name <- eval(substitute(var),eval(dt))
  tot <- sum(!is.na(var_name))
  na1 <- sum(is.na(var_name))
  m1 <- mean(var_name,na.rm=T)
  par(mfrow=c(2,2),oma=c(0,0,3,0))
  boxplot(var_name,main='With outliers')
  hist(var_name,main='With outliers',xlab=NA,ylab=NA)
  outlier <- var_name[var_name>230]
  mo <- mean(outlier)
  var_name <- ifelse(var_name %in% outlier,NA,var_name)
  boxplot(var_name,main='Without outliers')
  hist(var_name,main='Without outliers',xlab=NA,ylab=NA)
  title('Outlier Check',outer = TRUE)
  na2 <- sum(is.na(var_name))
  cat('Outliers identified:',na2-na1,'\n')
  cat('Proportion (%) of outliers:',round((na2-na1)/tot*100,1),'\n')
  cat('Mean of the outliers:',round(mo,2),'\n')
  m2 <- mean(var_name,na.rm=T)
  cat('Mean without removing outliers:',round(m1,2),'\n')
  cat('Mean if we remove outliers:',round(m2,2),'\n')
  response <- readline(prompt = 'Do you want to remove outliers
                        to replace with NA?[yes/no]:')
  if(response=='y'|response=='yes'){
    dt[as.character(substitute(var))] <- invisible(var_name)
    assign(as.character(as.list(match.call())$dt),dt,envir = .GlobalEnv)
    cat('Outliers successfully removed','\n')
    return(invisible(dt))
  }else{
    cat('Nothing changed','\n')
    return(invisible(var_name))
  }
}

生成一個(gè)數(shù)據(jù)框
運(yùn)行上述代碼

df <- data.frame(bp=c(sample(80:250,1000,replace = T),NA,390,100))
outlierHH(df,bp)

返回了圖并詢問是否將大于230的值用NA替換

上:未去除異常值 下:去除異常值

三、重復(fù)值

1. 針對(duì)向量

  • 1.1unique函數(shù)(常用)
x <- c(1,2,3,4,5,1,2,3)
unique(x)
# [1] 1 2 3 4 5
  • 1.2 duplicated函數(shù)(常用)
duplicated(x)
# [1] FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE
x[duplicated(x)]
# [1] 1 2 3
x[!duplicated(x)]
# [1] 1 2 3 4 5

2. 針對(duì)數(shù)據(jù)框

paste函數(shù)
使用paste函數(shù),把用來判斷是否存在重復(fù)值的變量貼在一起。對(duì)paste之后生成的test數(shù)據(jù)框進(jìn)行判斷,看它是否存在重復(fù)值就可以了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡(jiǎn)信或評(píng)論聯(lián)系作者。

推薦閱讀更多精彩內(nèi)容