13  缺失值

tidyverse中处理缺失值的函数主要来自dplyrtidyr

数据在拿到手时,通常不会是很工整的,通常会存在缺失值、格式混乱、数据有错误等问题,我们不能直接进行分析,需要对其进行预处理,也就是所谓的数据清洗。这个步骤非常重要,通常要占据整个分析或建模过程的75%的工作量。

13.1 缺失值

R中的缺失值用符号NA表示,空值用NULL表示。在处理缺失值时,需要将表示缺失值的一些其他符号(如na,NaN等)替换成NA在进行处理。

值得注意的是,R中的NA带有“传染性”,即只要有NA参与的计算,结果也会变成NA,所以对缺失值的处理非常重要。本节我们主要关注naniar包对缺失值的处理和探索的过程。

13.1.1 探索缺失值

  1. 缺失值统计
library(naniar)

# 查看缺失值个数
n_miss(airquality)
[1] 44
# 查看完整样本个数
n_complete(airquality)
[1] 874
# 查看缺失值占比
prop_miss_case(airquality)
[1] 0.2745098
# 查看缺失变量占比
prop_miss_var(airquality)
[1] 0.3333333
# 每行缺失值情况
miss_case_summary(airquality) # case代表行数,按照缺失值由多到少排序。
# A tibble: 153 × 3
    case n_miss pct_miss
   <int>  <int>    <dbl>
 1     5      2     33.3
 2    27      2     33.3
 3     6      1     16.7
 4    10      1     16.7
 5    11      1     16.7
 6    25      1     16.7
 7    26      1     16.7
 8    32      1     16.7
 9    33      1     16.7
10    34      1     16.7
# ℹ 143 more rows
# 行缺失汇总表
miss_case_table(airquality) # n_miss_in_case表示缺失值为该数的行数。
# A tibble: 3 × 3
  n_miss_in_case n_cases pct_cases
           <int>   <int>     <dbl>
1              0     111     72.5 
2              1      40     26.1 
3              2       2      1.31
# 每列(变量)缺失值情况
miss_var_summary(airquality)
# A tibble: 6 × 3
  variable n_miss pct_miss
  <chr>     <int>    <num>
1 Ozone        37    24.2 
2 Solar.R       7     4.58
3 Wind          0     0   
4 Temp          0     0   
5 Month         0     0   
6 Day           0     0   
# 列缺失汇总表
miss_var_table(airquality)
# A tibble: 3 × 3
  n_miss_in_var n_vars pct_vars
          <int>  <int>    <dbl>
1             0      4     66.7
2             7      1     16.7
3            37      1     16.7

以上所有函数均可以与group_by()连用,进行缺失值的分组探索。

我们也可以对缺失值进行可视化的探索。

gg_miss_var(airquality) # 每列缺失值情况

gg_miss_upset(airquality) # 各列缺失值的交集

  1. 对比缺失与非缺失数据

借助影子矩阵进行对比。影子矩阵与数据集维度相同,用于标记各个数据是否缺失(缺失用NA表示,不缺失用!NA表示)。

使用bind_shadow()函数将影子矩阵与原数据按列合并,进而开展下一步分析。

# 生成影子矩阵
aq_shadow <- bind_shadow(airquality)
aq_shadow
# A tibble: 153 × 12
   Ozone Solar.R  Wind  Temp Month   Day Ozone_NA Solar.R_NA Wind_NA Temp_NA
   <int>   <int> <dbl> <int> <int> <int> <fct>    <fct>      <fct>   <fct>  
 1    41     190   7.4    67     5     1 !NA      !NA        !NA     !NA    
 2    36     118   8      72     5     2 !NA      !NA        !NA     !NA    
 3    12     149  12.6    74     5     3 !NA      !NA        !NA     !NA    
 4    18     313  11.5    62     5     4 !NA      !NA        !NA     !NA    
 5    NA      NA  14.3    56     5     5 NA       NA         !NA     !NA    
 6    28      NA  14.9    66     5     6 !NA      NA         !NA     !NA    
 7    23     299   8.6    65     5     7 !NA      !NA        !NA     !NA    
 8    19      99  13.8    59     5     8 !NA      !NA        !NA     !NA    
 9     8      19  20.1    61     5     9 !NA      !NA        !NA     !NA    
10    NA     194   8.6    69     5    10 NA       !NA        !NA     !NA    
# ℹ 143 more rows
# ℹ 2 more variables: Month_NA <fct>, Day_NA <fct>
# 根据Ozone是否缺失,计算Solar.R的几个常用统计量
library(tidyverse)
aq_shadow %>%
  group_by(Ozone_NA) %>%
  summarise(across(
    Solar.R,
    list(
      \(x) mean(x, na.rm = T),
      \(x) sd(x, na.rm = T),
      \(x) var(x, na.rm = T),
      \(x) min(x, na.rm = T),
      \(x) max(x, na.rm = T)
    )
  ))
# A tibble: 2 × 6
  Ozone_NA Solar.R_1 Solar.R_2 Solar.R_3 Solar.R_4 Solar.R_5
  <fct>        <dbl>     <dbl>     <dbl>     <int>     <int>
1 !NA           185.      91.2     8309.         7       334
2 NA            190.      87.7     7690.        31       332

13.1.2 处理缺失值

  • 如果样本数据足够,且缺失样本比较少,可以使用na.omit()函数直接剔除包含NA的样本。

  • 如果只想剔除少部分包含NA的行,使用drop_na(df, <tidy-select>)直接删除。

  • 如果想删除大部分(但不是全部的)包含NA的行,需要使用如下代码。

  • 可以简单的使用tidy::fill()函数指定需要填补的变量(直接使用前值);或tidy::coalesce(x, default)(使x的缺失值用default取代)。

df_miss <- tibble(
  A = c(1, NA, NA, 2, NA, NA),
  B = c(1, 2, 3, 1, 2, 3),
  n = c(12, 10, NA, 15, NA, 14)
)
df_miss
# A tibble: 6 × 3
      A     B     n
  <dbl> <dbl> <dbl>
1     1     1    12
2    NA     2    10
3    NA     3    NA
4     2     1    15
5    NA     2    NA
6    NA     3    14
# 填补A列缺失值
df_miss %>%
  tidyr::fill(A)
# A tibble: 6 × 3
      A     B     n
  <dbl> <dbl> <dbl>
1     1     1    12
2     1     2    10
3     1     3    NA
4     2     1    15
5     2     2    NA
6     2     3    14
# 使用特定值填补特定列的缺失值
df_miss %>%
  mutate(
    x = dplyr::coalesce(n, 0)
  )
# A tibble: 6 × 4
      A     B     n     x
  <dbl> <dbl> <dbl> <dbl>
1     1     1    12    12
2    NA     2    10    10
3    NA     3    NA     0
4     2     1    15    15
5    NA     2    NA     0
6    NA     3    14    14
注记

有时,有可能缺失值被输入成了一个特殊数值,比如,x的值仅取正数,输入了-1就表示缺失值。在读入CSV文件时,如果所有缺失值都用-1表示,可以在read_csv()中加选项na = -1。如果不同列有不同的缺失值编码, 可以用na_if(x, na_value)x中等于na_value的值改为缺失值,可以用在mutate()中,如mutate(x = na_if(x, -1))