R語言基礎8--R語言缺失值、異常值和重復值的識別與處理


R語言基礎系列:


一、缺失值

1. 缺失值的識別和處理

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

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函數的簡單使用:

判斷一個數據框中有沒有缺失值,有多少缺失值

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

以iris數據集為基礎生成一個新的含缺失值的數據集iris_na

#在iris數據集的1-4列(數值型變量),1到最后一行中隨機抽取五個值,用缺失值NA替換(i in 1:4是在1到4列中對i值做一個遍歷)
iris_na <- iris
for(i in 1:4){
  iris_na[sample(1:nrow(iris),5),i]=NA
}
1.2 使用sapply函數?which來找到這5個缺失值
# which就是一個指針函數,意思是在哪里
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
# 每一列的缺失值的行數被顯示出來 
1.3 使用psych包中的describe函數(describe函數可以極大程度的返回了數據集的基本統計值)??
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個值,說明存在5個缺失值
1.4 計算缺失值的比例
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
# 缺失值的和/總行數
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默認是na.omit,因此在使用lm求回歸時寫不寫這個參數都可以。

2. 填補缺失值

??在進行缺失值的填補的時候一定要緊密結合數據背景和專業背景,否則易出現矯枉過正,適得其反。

2.1 簡單操作系列:使用均值填補缺失值
# 求均值
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列對進行遍歷,判斷數據框中有沒有缺失值,返回的是TRUE或FALSE。iris_na[is.na(iris_na[,I],i]就相當于根據is.na(iris_na[,i]), i的結果從iris_na中進行提取

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

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

#缺失值填補
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 填補缺失值的高級操作

演示數據集準備

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

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

對缺失值進行操作

  • 2.2.1 mice包??

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

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表示不缺失。最下面一行是每一個變量中缺失值的總數。最下面一行顯示rad和ptratio各有80個缺失,共有160個缺失。左邊一列359表示359個觀測都沒有缺失,有67個觀測rad變量缺失,ptratio沒有缺失。有67個變量ptratio缺失,rad沒有缺失。有13個觀測red和ptratio都缺失。

對缺失值進行插補

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

#  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()函數用于接受前面生成的模型,并生成一個完整數據。

mice_output <- complete(mice_mod)
anyNA(mice_output)
[1] FALSE
# 已經不存在缺失值了
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

檢驗替換后的值和原本數據框中的值的是否一樣,結果發現,預測的值80%都和未被NA替換的值一樣,精度很高(高于均數、眾數、中位數等等)。

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()函數(較粗略,不推薦)
# 使用均值進行插補
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 
# 帶星號表示是插補的值

#缺失值替換
BostonHousing$ptratio <- NULL
BostonHousing$im_mean <- im_mean
  • 2.2.3VIM包??
    使用本身就含有缺失值的airquality數據集進行操作
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進行查看

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個缺失值

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

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表示顯示確實值的比例
# sortVars=TRUE表示對變量按照缺失值的多少進行排序
# labels=names(airquality)定義圖的標題
# cex.axis=0.7,gap=3定義坐標軸的大小和圖之間的間距

#  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是設置非缺失值和缺失值顏的參數。
# numbers=TRUE是顯示缺失值和非缺失值的比例。
# sortVars=TRUE是根據缺失值的多少進行排序。
# labels是對生成的圖進行標簽化。
# cex.axis是定義坐標軸的大小。
綠色代表缺失,左圖中左邊兩個變量有缺失,右邊三個沒有缺失。柱子的長短顯示了缺失值的多少。右邊的圖反應缺失值的模式。Ozone單獨缺失的比例是0.229,Solar.R單獨缺失的比例是0.033,兩個一起缺失的比例是0.013。

marginplot()是另一個對缺失值進行可視化的函數

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

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

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

插補之后

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

#上面一部分顯示原有缺失值已被插補,下面一部分是判斷原有的值是否為缺省值,FALSE表示不是缺省值,TRUE表示是缺省值,但已經被填補。

regressionImp函數中還有個參數family,選'auto'。因為因變量包括連續型和離散型等,他們采取的回歸方式是不一樣的。auto模式可以自動采取合適的方法。

二、異常值

生成一個數據集

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

處理異常值的自定義函數

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

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))
  }
}

生成一個數據框
運行上述代碼

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

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

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

三、重復值

1. 針對向量

  • 1.1unique函數(常用)
x <- c(1,2,3,4,5,1,2,3)
unique(x)
# [1] 1 2 3 4 5
  • 1.2 duplicated函數(常用)
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. 針對數據框

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
禁止轉載,如需轉載請通過簡信或評論聯系作者。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,634評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,653評論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,063評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,835評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,235評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,459評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,000評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,819評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,004評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,257評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,676評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,937評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,717評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,003評論 2 374

推薦閱讀更多精彩內容