第 45 章 tidyverse中的缺失值
今天我们聊聊数据处理中的缺失值。
45.1 什么是缺失值?
我们先导入企鹅数据
## # A tibble: 344 × 8
## species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
## <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 Adelie Torgersen 39.1 18.7 181 3750
## 2 Adelie Torgersen 39.5 17.4 186 3800
## 3 Adelie Torgersen 40.3 18 195 3250
## 4 Adelie Torgersen NA NA NA NA
## 5 Adelie Torgersen 36.7 19.3 193 3450
## 6 Adelie Torgersen 39.3 20.6 190 3650
## 7 Adelie Torgersen 38.9 17.8 181 3625
## 8 Adelie Torgersen 39.2 19.6 195 4675
## 9 Adelie Torgersen 34.1 18.1 193 3475
## 10 Adelie Torgersen 42 20.2 190 4250
## # ℹ 334 more rows
## # ℹ 2 more variables: sex <chr>, year <dbl>
我们看到第4行第3列开始,出现若干NA
,这里的NA
就是缺失值,NA
的意思就是 not available.
注意区分 “NA” 和 NA
-
“NA” 有引号的是字符串
- NA 是R里的特殊标记
在R console中执行 ?"NA"
,我们看到第一行
NA is a logical constant of length 1 which contains a missing value indicator.
也就说,NA代表了数据缺失的一种逻辑状态(另外两个逻辑值是TRUE, FALSE),NA有两层含义:
- 它是逻辑值
- 代表着缺失值
45.2 有关NA的计算
- 数值运算时
一般情况下,与 NA
的数值计算结果也是 NA
,
c(NA, 1) + 2
## [1] NA 3
- 逻辑运算时
逻辑运算中TRUE
为真,FALSE
为假,而NA
会被认为未知(即真假难辨)7。
isTRUE(NA)
## [1] FALSE
isFALSE(NA)
## [1] FALSE
既不是真,也不是假,真假难辨。因此,在逻辑运算时,可以按下表指引
TRUE | NA | FALSE |
---|---|---|
真 | 不能确定真假 | 假 |
# Some logical operations do not return NA
c(TRUE, FALSE) & NA
## [1] NA FALSE
c(TRUE, FALSE) | NA
## [1] TRUE NA
可以看到,TRUE & NA 的结果为 NA(而不是FALSE),是因为NA的意思是“不能确定真假”,即有可能真也有可能假,介于真和假之间。因此TRUE 与 NA的逻辑和(即TRUE & NA)返回NA;而FALSE 与 NA的逻辑和(即FALSE & NA) 则返回FALSE。逻辑或的情形也是类似的。
45.4 强制转换
前面提到 NA
是一个与TRUE
和FALSE
并列的逻辑值,比如
## [1] "logical"
它的结果变成了”logical”.
但如果 NA 放在数值型的向量中,
## [1] "numeric"
它的结果却变成了”numeric”
如果 NA 放在字符串的向量中,
## [1] "character"
它的结果却变成了”character”
为什么会出现这种诡异的现象?究其原因,还在于NA的属性上,代表数据缺失的逻辑值,即数据类型是逻辑型。
c(TRUE, NA, FALSE)
## [1] TRUE NA FALSE
## [1] "logical"
在第 4 章中,我们提到,把不同类型的数据用c()
组合成向量时,因为c()
函数要求数据类型必须一致,因此就会发生强制转换。比如当逻辑型变量和数值型变量组合在一起时,逻辑型会强制转换成数值型。
c(1, 2, TRUE, 4)
## [1] 1 2 1 4
## [1] "numeric"
TRUE会转换成1,FALSE会转换成0. 那么此时逻辑型的 NA 会转换成数值型的 NA_real_
逻辑型 | 转换成数值型 |
---|---|
TRUE | 1 |
NA | NA_real_ |
FALSE | 0 |
c(1, 2, NA, 4)
## [1] 1 2 NA 4
## [1] "numeric"
c(1, 2, NA_real_, 4)
## [1] 1 2 NA 4
## [1] "numeric"
当逻辑型变量和字符串型变量组合在一起时,逻辑型会强制转换成字符串型。
逻辑型 | 转换成字符串型 |
---|---|
TRUE | “TRUE” |
NA | NA_character_ |
FALSE | “FALSE” |
c("1", "2", TRUE, "4")
## [1] "1" "2" "TRUE" "4"
c("1", "2", NA, "4")
## [1] "1" "2" NA "4"
c("1", "2", NA_character_, "4")
## [1] "1" "2" NA "4"
除了逻辑型NA
, 数值型NA_real_
, 字符串型NA_character_
外, 还有整数型NA_integer_
, 和复数型NA_complex
. 我们再看下面的例子
c(TRUE, NA) %>%
purrr::map(., ~is.logical(.))
## [[1]]
## [1] TRUE
##
## [[2]]
## [1] TRUE
c("a", NA, NA_character_) %>%
purrr::map(., ~is.character(.))
## [[1]]
## [1] TRUE
##
## [[2]]
## [1] TRUE
##
## [[3]]
## [1] TRUE
c(123, NA, NA_real_) %>%
purrr::map(., ~is.numeric(.))
## [[1]]
## [1] TRUE
##
## [[2]]
## [1] TRUE
##
## [[3]]
## [1] TRUE
c(NA_real_, NA_complex_, NA_character_, NA_integer_, NA) %>% # coercion to character type
purrr::map(., ~is.character(.))
## [[1]]
## [1] TRUE
##
## [[2]]
## [1] TRUE
##
## [[3]]
## [1] TRUE
##
## [[4]]
## [1] TRUE
##
## [[5]]
## [1] TRUE
45.5 如果统计有多少NA?
在实际的数据处理中,没有人愿意把问题搞复杂,一般情况下会先预处理,比如,剔除掉或者用其他值替换掉。不管是一删了之,还是采用插值替换,都有必要了解下数据中多少NA,这样才决定采用什么样的预处理办法。
常用的方法:
先用is.na()
判断出是否为缺失值,缺失值是TRUE,不是缺失值为FALSE;然后TRUE/FALSE转换成数值,即TRUE -> 1; FALSE -> 0;最后把所有的1加起来,就知道数据中有多少个缺失值。
具体代码为
## [1] 1
偷懒可以这样写
## [1] 1
当然也可以自定义一个函数,
## [1] 1
45.6 应用到tidyverse中
回到本章开始的企鹅数据
penguins$bill_length_mm %>% sum_of_na()
## [1] 2
用到dplyr函数中
## # A tibble: 1 × 2
## N1 N2
## <int> <int>
## 1 2 2
一次性统计所有列
penguins %>% summarise(
across(everything(), sum_of_na)
)
## # A tibble: 1 × 8
## species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
## <int> <int> <int> <int> <int> <int>
## 1 0 0 2 2 2 2
## # ℹ 2 more variables: sex <int>, year <int>
更偷懒的办法,也更直观(再次感受到R的美!)
## # A tibble: 1 × 8
## species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
## <int> <int> <int> <int> <int> <int>
## 1 0 0 2 2 2 2
## # ℹ 2 more variables: sex <int>, year <int>
数据框的一列中每个元素的数据类型是要求相同的,这是构建数据框的基本要求。因此,在dplyr中mutate()
函数创建数据框的新列时, 这一列的元素必须是同一种类型,如果遇到新列中包含NA,也要确保NA的类型与其它元素的类型要一致,比如其它元素是字符串,那么就应该使用字符串类型的缺失值,即NA_character_.
我来看下面这个例子:
## # A tibble: 6 × 1
## x
## <dbl>
## 1 1
## 2 3
## 3 6
## 4 NA
## 5 8
## 6 NA
d %>% mutate(
is_even = case_when(
x %% 2 == 0 ~ "even",
x %% 2 == 1 ~ "not even",
TRUE ~ NA # wrong
)
)
上面这个代码中,本意是希望构建一个新列存储(“even”, “not even”)字符串;而NA
是逻辑型的,类型不一致,因此会报错。
正确的写法是使用NA_character_
d %>% mutate(
is_even = case_when(
x %% 2 == 0 ~ "even",
x %% 2 == 1 ~ "not even",
TRUE ~ NA_character_
)
)
## # A tibble: 6 × 2
## x is_even
## <dbl> <chr>
## 1 1 not even
## 2 3 not even
## 3 6 even
## 4 NA <NA>
## 5 8 even
## 6 NA <NA>
45.7 思考
上面例子中的
dplyr::case_when()
换做dplyr::if_else()
函数,应该怎么写?企鹅数据中,找出有缺失值的行,有一个NA也算。