美文网首页统计分析方法R语言
R语言缺失值、异常值和重复值的识别与处理

R语言缺失值、异常值和重复值的识别与处理

作者: Hayley笔记 | 来源:发表于2021-06-07 19:44 被阅读0次

    一、缺失值

    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数据框进行判断,看它是否存在重复值就可以了。

    相关文章

      网友评论

        本文标题:R语言缺失值、异常值和重复值的识别与处理

        本文链接:https://www.haomeiwen.com/subject/bznteltx.html