-
Notifications
You must be signed in to change notification settings - Fork 220
/
tidyverse_beauty_of_across2.Rmd
167 lines (103 loc) · 3.77 KB
/
tidyverse_beauty_of_across2.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
# tidyverse中的across()之美2 {#tidyverse-beauty-of-across2}
```{r, include=FALSE}
knitr::opts_chunk$set(
echo = TRUE,
warning = FALSE,
message = FALSE,
fig.showtext = TRUE
)
```
```{r beauty-of-across2-1}
library(tidyverse)
library(palmerpenguins)
```
## 曾经的痛点
dplyr 1.0.0 引入了`across()`函数,让我们再次感受到了dplyr的强大和人性化。
`across()`函数与`summarise()`和`mutate()`函数配合起来使用,非常方便(参考第 \@ref(tidyverse-beauty-of-across1) 章),
但与`filter()`函数不是很理想,比如我们想**筛选数据框有缺失值的行**
```{r beauty-of-across2-2}
penguins %>%
filter( across(everything(), any_vars(is.na(.)))
)
```
代码能运行,但结果明显不正确。我搜索了很久,发现只能用dplyr 1.0.0之前的`filter_all()`函数实现,
```{r beauty-of-across2-3}
penguins %>%
filter_all( any_vars(is.na(.)) )
```
多少让人感觉,在追求简约道路上,还是有美中不足。
## dplyr 1.0.4: if_any() and if_all()
如今,dplyr 1.0.4推出了 `if_any()` and `if_all()` 两个函数,正是弥补这个缺陷
```{r beauty-of-across2-41, out.width = '90%', echo = FALSE}
knitr::include_graphics("images/HotlineDrake10.jpg")
```
```{r beauty-of-across2-4}
penguins %>%
filter(if_any(everything(), is.na))
```
从函数形式上看,`if_any` 对应着 `across`的地位,
```r
across(.cols = everything(), .fns = NULL, ..., .names = NULL)
if_any(.cols, .fns = NULL, ..., .names = NULL)
if_all(.cols, .fns = NULL, ..., .names = NULL)
```
这就意味着**列方向我们有across(),行方向我们有if_any()/if_all()了**,可谓 纵横武林,倚天屠龙、谁与争锋?
## 案例赏析
下面通过一些例子展示下这两个新函数,其中一部分案例来自[官网](https://www.tidyverse.org/blog/2021/02/dplyr-1-0-4-if-any/)。
- 筛选有缺失值的行
```{r beauty-of-across2-5}
penguins %>%
filter(if_any(everything(), is.na))
```
或者更简单
```{r}
penguins %>%
filter(if_any(.fns = is.na))
```
- 筛选全部是缺失值的行
```{r beauty-of-across2-6}
penguins %>%
filter(if_all(everything(), is.na))
```
- 筛选企鹅嘴峰(长度和厚度)全部大于21mm的行
```{r beauty-of-across2-7}
penguins %>%
filter(if_all(contains("bill"), ~ . > 21))
```
当然可以弄成更骚一点喔
```{r beauty-of-across2-71}
penguins %>%
filter(if_all(contains("bill"), `>`, 21))
```
- 筛选企鹅嘴峰(长度或者厚度)大于21mm的行
```{r beauty-of-across2-8}
penguins %>%
filter(if_any(contains("bill"), ~ . > 21))
```
- 在指定的列(嘴峰长度和厚度)中检查每行的元素,如果这些元素都大于各自所在列的均值,就保留下来
```{r beauty-of-across2-9}
bigger_than_mean <- function(x) {
x > mean(x, na.rm = TRUE)
}
penguins %>%
filter(if_all(contains("bill"), bigger_than_mean))
```
- 在指定的列(嘴峰长度和嘴峰厚度)中检查每行的元素,如果这些元素都大于各自所在列的均值,就"both big";如果这些元素有一个大于自己所在列的均值,就"one big",(注意case_when中if_all要在if_any之前)
```{r beauty-of-across2-10}
penguins %>%
filter(!is.na(bill_length_mm)) %>%
mutate(
category = case_when(
if_all(contains("bill"), bigger_than_mean) ~ "both big",
if_any(contains("bill"), bigger_than_mean) ~ "one big",
TRUE ~ "small"
))
```
```{r beauty-of-across2-23, echo = F}
# remove the objects
# ls() %>% stringr::str_flatten(collapse = ", ")
#rm(cutoffs, d1, d2, df, mult, std, weights, replace_col_max)
```
```{r beauty-of-across2-24, echo = F, message = F, warning = F, results = "hide"}
pacman::p_unload(pacman::p_loaded(), character.only = TRUE)
```