dplyr中的列式操作:across()函数详解
dplyr 项目地址: https://gitcode.com/gh_mirrors/dpl/dplyr
引言
在数据分析过程中,我们经常需要对数据框中的多个列执行相同的操作。传统方法是通过复制粘贴代码来逐个处理每一列,但这不仅效率低下,而且容易出错。dplyr包中的across()
函数提供了一种优雅的解决方案,可以同时对多个列执行相同的操作。
across()函数基础
across()
函数有两个主要参数:
.cols
:选择要操作的列,支持类似select()
函数的列选择语法.fns
:要应用于每个列的函数或函数列表
基本用法示例
# 计算所有数值型列的标准差
starwars %>%
summarise(across(where(is.numeric), sd))
# 按物种分组后,计算性别、性别认同和母星的唯一值数量
starwars %>%
group_by(species) %>%
filter(n() > 1) %>%
summarise(across(c(sex, gender, homeworld), n_distinct))
across()
会自动忽略分组变量,避免意外修改它们。
高级用法
应用多个函数
我们可以通过传递命名函数列表来对每个列应用多个函数:
min_max <- list(
min = ~min(.x, na.rm = TRUE),
max = ~max(.x, na.rm = TRUE)
)
starwars %>% summarise(across(where(is.numeric), min_max))
自定义结果列名
使用.names
参数可以控制结果列名的生成方式,它接受一个glue格式的字符串:
starwars %>% summarise(across(where(is.numeric), min_max, .names = "{.fn}.{.col}"))
访问当前列名
在函数内部,可以使用cur_column()
获取当前正在处理的列名:
df <- tibble(x = 1:3, y = 3:5, z = 5:7)
mult <- list(x = 1, y = 10, z = 100)
df %>% mutate(across(all_of(names(mult)), ~ .x * mult[[cur_column()]]))
与其他动词结合使用
across()
不仅可以与summarise()
一起使用,还可以与任何支持数据掩码的dplyr动词配合:
与mutate()结合
# 将所有数值型列缩放到0-1范围
rescale01 <- function(x) {
rng <- range(x, na.rm = TRUE)
(x - rng[1]) / (rng[2] - rng[1])
}
df %>% mutate(across(where(is.numeric), rescale01))
与filter()结合
filter()
需要特殊处理,因为它需要额外的步骤来组合结果。dplyr提供了两个配套函数:
if_any()
:保留在至少一个选定列中满足条件的行if_all()
:保留在所有选定列中都满足条件的行
# 保留所有列都不含NA的行
starwars %>%
filter(if_all(everything(), ~ !is.na(.x)))
与传统方法的比较
在dplyr的早期版本中,我们使用_if
、_at
和_all
后缀的函数来处理列式操作。across()
提供了更统一和强大的替代方案:
- 表达能力更强,可以表达以前无法实现的汇总操作
- 减少了需要记忆的函数数量
- 统一了选择语义,支持复合选择条件
- 不再需要手动引用变量名
代码转换指南
将旧代码转换为使用across()
的步骤:
- 去掉函数名中的
_if
、_at
或_all
后缀 - 使用
across()
:- 对于
_if
,将第二个参数用where()
包装 - 对于
_at
,去掉vars()
- 对于
_all
,使用everything()
- 对于
# 旧代码
df %>% mutate_if(is.numeric, ~mean(.x, na.rm = TRUE))
# 新代码
df %>% mutate(across(where(is.numeric), ~mean(.x, na.rm = TRUE)))
注意事项
- 当组合数值型汇总时,要注意
n()
等函数的影响 across()
在mutate()
中会一次性应用所有转换,这与旧函数逐个应用的行为不同- 不能直接在
select()
或rename()
中使用across()
,应改用rename_with()
结语
across()
函数是dplyr中处理列式操作的强大工具,它简化了代码,提高了可读性,并提供了更大的灵活性。通过掌握across()
,你可以更高效地处理数据操作任务,写出更简洁、更易维护的代码。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考