41

across 之美(二)

across 进阶技巧

Tidyverse 篇
R
library(tidyverse)
library(palmerpenguins)

曾经的痛点

dplyr 1.0.0 引入了across()函数,让我们再次感受到了dplyr的强大和人性化。 across()函数与summarise()mutate()函数配合起来使用,非常方便(参考相关章节), 但与filter()函数不是很理想,比如我们想筛选数据框有缺失值的行

R
penguins %>% 
  filter( across(everything(), any_vars(is.na(.)))
)

代码能运行,但结果明显不正确。我搜索了很久,发现只能用dplyr 1.0.0之前的filter_all()函数实现,

R
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
penguins %>% 
  filter(if_any(everything(), is.na))

从函数形式上看,if_any 对应着 across的地位,

[R表达式]```


这就意味着**列方向我们有across(),行方向我们有if_any()/if_all()了**,可谓 纵横武林,倚天屠龙、谁与争锋?


## 案例赏析

下面通过一些例子展示下这两个新函数,其中一部分案例来自[官网](https://www.tidyverse.org/blog/2021/02/dplyr-1-0-4-if-any/)。


- 筛选有缺失值的行

penguins %>% filter(if_any(everything(), is.na))

TEXT

或者更简单

penguins %>% filter(if_any(.fns = is.na))

TEXT


- 筛选全部是缺失值的行

penguins %>% filter(if_all(everything(), is.na))

TEXT





- 筛选企鹅嘴峰(长度和厚度)全部大于21mm的行

penguins %>% filter(if_all(contains("bill"), ~ . > 21))

TEXT


当然可以弄成更骚一点喔

penguins %>% filter(if_all(contains("bill"), >, 21))

TEXT


- 筛选企鹅嘴峰(长度或者厚度)大于21mm的行

penguins %>% filter(if_any(contains("bill"), ~ . > 21))

TEXT






- 在指定的列(嘴峰长度和厚度)中检查每行的元素,如果这些元素都大于各自所在列的均值,就保留下来

bigger_than_mean <- function(x) { x > mean(x, na.rm = TRUE) }

penguins %>% filter(if_all(contains("bill"), bigger_than_mean))

TEXT


- 在指定的列(嘴峰长度和嘴峰厚度)中检查每行的元素,如果这些元素都大于各自所在列的均值,就"both big";如果这些元素有一个大于自己所在列的均值,就"one big",(注意case_when中if_all要在if_any之前)

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" ))

TEXT




TEXT


pacman::p_unload(pacman::p_loaded(), character.only = TRUE)

TEXT