-
Notifications
You must be signed in to change notification settings - Fork 220
/
tidyverse_NA.Rmd
344 lines (208 loc) · 7.75 KB
/
tidyverse_NA.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# tidyverse中的缺失值 {#tidyverse-NA}
```{r, include=FALSE}
knitr::opts_chunk$set(
echo = TRUE,
warning = FALSE,
message = FALSE,
fig.showtext = TRUE
)
```
今天我们聊聊数据处理中的缺失值。
## 什么是缺失值?
我们先导入企鹅数据
```{r tidyverse-NA-1, message=FALSE, warning=FALSE}
library(tidyverse)
penguins <- read_csv(here::here("demo_data", "penguins.csv"))
penguins
```
我们看到第4行第3列开始,出现若干`NA`,这里的`NA`就是**缺失值**,`NA`的意思就是 not available.
```{block tidyverse-NA-2, type="danger"}
注意区分 "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有两层含义:
- 它是逻辑值
- 代表着缺失值
## 有关NA的计算
- 数值运算时
一般情况下,与 `NA` 的数值计算结果也是 `NA`,
```{r tidyverse-NA-3}
c(NA, 1) + 2
```
- 逻辑运算时
逻辑运算中`TRUE`为真,`FALSE`为假,而`NA`会被认为未知(即真假难辨)^[https://tianyishi2001.github.io/r-and-tidyverse-book/logical-operation.html]。
```{r tidyverse-NA-4}
isTRUE(NA)
isFALSE(NA)
```
既不是真,也不是假,真假难辨。因此,在逻辑运算时,可以按下表指引
| TRUE | NA | FALSE |
|:----- |:-------------:|:------- |
| 真 | 不能确定真假 | 假 |
```{r tidyverse-NA-5}
# Some logical operations do not return NA
c(TRUE, FALSE) & NA
c(TRUE, FALSE) | NA
```
可以看到,TRUE & NA 的结果为 NA(而不是FALSE),是因为NA的意思是“不能确定真假”,即有可能真也有可能假,介于真和假之间。因此TRUE 与 NA的逻辑和(即TRUE & NA)返回NA;而FALSE 与 NA的逻辑和(即FALSE & NA) 则返回FALSE。逻辑或的情形也是类似的。
## 如何判断NA?
找出数据中的缺失值,可以用`is.na()`函数
```{r tidyverse-NA-6}
c(1, 2, NA, 4) %>% is.na()
```
## 强制转换
前面提到 `NA` 是一个与`TRUE`和`FALSE`并列的逻辑值,比如
```{r tidyverse-NA-7}
c(TRUE, FALSE, NA) %>% class()
```
它的结果变成了"logical".
但如果 NA 放在数值型的向量中,
```{r tidyverse-NA-8}
c(1, 2, NA, 4) %>% class()
```
它的结果却变成了"numeric"
如果 NA 放在字符串的向量中,
```{r tidyverse-NA-9}
c("1", "2", NA, "4") %>% class()
```
它的结果却变成了"character"
为什么会出现这种**诡异**的现象?究其原因,还在于NA的属性上,代表数据缺失的逻辑值,即数据类型是逻辑型。
```{r tidyverse-NA-10}
c(TRUE, NA, FALSE)
c(TRUE, NA, FALSE) %>% class()
```
在第 \@ref(baseR-vectors) 章中,我们提到,把不同类型的数据用`c()`组合成向量时,因为`c()` 函数要求数据类型必须一致,因此就会发生**强制转换**。比如当逻辑型变量和数值型变量组合在一起时,**逻辑型**会强制转换成**数值型**。
```{r tidyverse-NA-11}
c(1, 2, TRUE, 4)
c(1, 2, TRUE, 4) %>% class()
```
TRUE会转换成1,FALSE会转换成0. 那么此时逻辑型的 NA 会转换成数值型的 NA_real_
| 逻辑型 | 转换成数值型 |
|:------- |:------------- |
| TRUE | 1 |
| NA | NA_real_ |
| FALSE | 0 |
```{r tidyverse-NA-12}
c(1, 2, NA, 4)
c(1, 2, NA, 4) %>% class()
c(1, 2, NA_real_, 4)
c(1, 2, NA_real_, 4) %>% class()
```
当逻辑型变量和字符串型变量组合在一起时,**逻辑型**会强制转换成**字符串型**。
| 逻辑型 | 转换成字符串型 |
|:------- |:--------------- |
| TRUE | "TRUE" |
| NA | NA_character_ |
| FALSE | "FALSE" |
```{r tidyverse-NA-13}
c("1", "2", TRUE, "4")
c("1", "2", NA, "4")
c("1", "2", NA_character_, "4")
```
除了逻辑型`NA`, 数值型`NA_real_`, 字符串型`NA_character_`外, 还有整数型`NA_integer_`, 和复数型`NA_complex`. 我们再看下面的例子
```{r tidyverse-NA-14}
c(TRUE, NA) %>%
purrr::map(., ~is.logical(.))
c("a", NA, NA_character_) %>%
purrr::map(., ~is.character(.))
c(123, NA, NA_real_) %>%
purrr::map(., ~is.numeric(.))
c(NA_real_, NA_complex_, NA_character_, NA_integer_, NA) %>% # coercion to character type
purrr::map(., ~is.character(.))
```
## 如果统计有多少NA?
在实际的数据处理中,没有人愿意把问题搞复杂,一般情况下会先**预处理**,比如,剔除掉或者用其他值替换掉。不管是一删了之,还是采用插值替换,都有必要了解下数据中多少NA,这样才决定采用什么样的预处理办法。
```{r tidyverse-NA-15, eval=FALSE, include=FALSE}
c(TRUE, TRUE, FALSE, FALSE) %>% as.integer() %>% sum()
c(TRUE, TRUE, FALSE, FALSE) %>% sum()
```
常用的方法:
先用`is.na()`判断出是否为缺失值,缺失值是TRUE,不是缺失值为FALSE;然后TRUE/FALSE转换成数值,即TRUE -> 1; FALSE -> 0;最后把所有的1加起来,就知道数据中有多少个缺失值。
具体代码为
```{r tidyverse-NA-17}
c(1, 2, NA, 4) %>% is.na() %>% as.integer() %>% sum()
```
偷懒可以这样写
```{r tidyverse-NA-18}
c(1, 2, NA, 4) %>% is.na() %>% sum()
```
当然也可以自定义一个函数,
```{r tidyverse-NA-19}
sum_of_na <- function(x){
sum(is.na(x))
}
c(1, 2, NA, 4) %>% sum_of_na()
```
## 应用到tidyverse中
回到本章开始的企鹅数据
```{r tidyverse-NA-20}
penguins$bill_length_mm %>% sum_of_na()
```
用到dplyr函数中
```{r tidyverse-NA-21}
penguins %>% summarise(
N1 = sum_of_na(bill_length_mm),
N2 = sum_of_na(bill_depth_mm)
)
```
一次性统计所有列
```{r tidyverse-NA-22}
penguins %>% summarise(
across(everything(), sum_of_na)
)
```
更偷懒的办法,也更直观(再次感受到R的美!)
```{r tidyverse-NA-23}
penguins %>% summarise(
across(everything(), ~sum(is.na(.x)))
)
```
数据框的一列中每个元素的数据类型是要求相同的,这是构建数据框的基本要求。因此,在dplyr中`mutate()`函数创建数据框的**新列**时, 这一列的元素必须是同一种类型,如果遇到新列中包含NA,也要确保NA的类型与其它元素的类型要一致,比如其它元素是字符串,那么就应该使用字符串类型的缺失值,即NA_character_.
我来看下面这个例子:
```{r tidyverse-NA-24}
d <- tibble(x = c(1, 3, 6, NA, 8, NA))
d
```
```{r tidyverse-NA-25, which-NA-use, eval=FALSE}
d %>% mutate(
is_even = case_when(
x %% 2 == 0 ~ "even",
x %% 2 == 1 ~ "not even",
TRUE ~ NA # wrong
)
)
```
上面这个代码中,本意是希望构建一个新列存储("even", "not even")字符串;而`NA`是逻辑型的,类型不一致,因此会报错。
正确的写法是使用`NA_character_`
```{r tidyverse-NA-26}
d %>% mutate(
is_even = case_when(
x %% 2 == 0 ~ "even",
x %% 2 == 1 ~ "not even",
TRUE ~ NA_character_
)
)
```
## 思考
- 上面例子中的`dplyr::case_when()`换做`dplyr::if_else()`函数,应该怎么写?
- 企鹅数据中,找出有缺失值的行,有一个NA也算。
```{r tidyverse-NA-27, eval=FALSE, include=FALSE}
penguins %>% filter_all(
any_vars(is.na(.))
)
```
## 更多
注意区分NA 和 Inf, NaN, NULL
- Inf = 无穷大,比如 `pi / 0 %>% is.infinite()`
- NaN = 不是一个数(Not a Number), 比如 `0 / 0 %>% is.nan() `, `sqrt(-1) %>% is.nan() `
- NULL = 空值,比如 `c() %>% is.null()`
```{r tidyverse-NA-29, echo = F}
# remove the objects
rm(penguins, d, sum_of_na)
```
```{r tidyverse-NA-30, echo = F, message = F, warning = F, results = "hide"}
pacman::p_unload(pacman::p_loaded(), character.only = TRUE)
```