dplyr中的列式操作:across()函数详解
【免费下载链接】dplyr 项目地址: https://gitcode.com/gh_mirrors/dpl/dplyr
还在为需要对数据框的多个列重复相同操作而烦恼吗?复制粘贴不仅枯燥乏味,还容易出错。dplyr的across()函数正是解决这一痛点的利器,它让你能够轻松地对多个列应用相同的转换操作。
通过本文,你将掌握:
across()函数的核心语法和工作原理- 多种列选择方式和函数应用技巧
- 输出列命名的灵活控制方法
- 与
if_any()、if_all()的配合使用 - 实际场景中的最佳实践和常见陷阱
across()函数基础
across()函数是dplyr包中用于列式操作的核心函数,它允许你在summarise()、mutate()等数据掩码函数中使用select()的选择语义。
基本语法结构
across(.cols, .fns, ..., .names = NULL, .unpack = FALSE)
| 参数 | 说明 | 示例 |
|---|---|---|
.cols | 要转换的列选择 | c(Sepal.Length, Sepal.Width) |
.fns | 要应用的函数 | mean, ~ mean(.x, na.rm = TRUE) |
.names | 输出列命名模板 | "mean_{.col}" |
.unpack | 是否解包数据框列 | TRUE/FALSE |
基础使用示例
library(dplyr)
# 对字符型列计算唯一值数量
starwars %>%
summarise(across(where(is.character), n_distinct))
# 对数值型列应用多个函数
min_max <- list(
min = ~min(.x, na.rm = TRUE),
max = ~max(.x, na.rm = TRUE)
)
starwars %>% summarise(across(where(is.numeric), min_max))
列选择技巧
across()支持多种列选择方式,与select()函数的选择语义完全一致。
按位置选择
iris %>% mutate(across(1:2, round)) # 选择第1-2列
按名称选择
iris %>% mutate(across(c(Sepal.Length, Sepal.Width), round))
按条件选择
# 选择所有数值型列
iris %>% mutate(across(where(is.numeric), round))
# 选择名称包含"Sepal"的列
iris %>% mutate(across(starts_with("Sepal"), round))
# 复合选择:数值型且名称以"x"开头
iris %>% mutate(across(where(is.numeric) & starts_with("Sepal"), round))
使用外部向量选择
cols <- c("Sepal.Length", "Petal.Width")
iris %>% mutate(across(all_of(cols), round))
# 命名向量可以控制输出列名
names(cols) <- tolower(cols)
iris %>% mutate(across(all_of(cols), round))
函数应用方式
.fns参数支持多种函数定义方式,满足不同场景需求。
单个函数
# 使用内置函数
iris %>% summarise(across(where(is.numeric), mean))
# 使用匿名函数
iris %>% summarise(across(where(is.numeric), ~ mean(.x, na.rm = TRUE)))
多个函数列表
# 命名函数列表
iris %>% summarise(across(where(is.numeric),
list(mean = mean, sd = sd, n_miss = ~ sum(is.na(.x)))))
# 输出结果包含 mean_Sepal.Length, sd_Sepal.Length, n_miss_Sepal.Length 等列
访问当前列信息
在函数内部可以使用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()]]))
输出列命名控制
.names参数使用glue语法模板来控制输出列名,提供灵活的命名方式。
基本命名模板
# 默认命名:{列名}_{函数名}
iris %>% summarise(across(where(is.numeric), list(mean = mean, sd = sd)))
# 自定义命名模板
iris %>% summarise(across(where(is.numeric), mean, .names = "mean_{.col}"))
iris %>% summarise(across(where(is.numeric), list(mean = mean, sd = sd),
.names = "{.col}.{.fn}"))
使用局部变量
local({
prefix <- "summary"
iris %>% summarise(across(where(is.numeric), mean, .names = "{prefix}_{.col}"))
})
与其他dplyr动词配合
across()可以与大多数dplyr动词配合使用,但需要注意一些特殊用法。
与mutate()配合
# 对数值型列进行标准化
rescale01 <- function(x) {
rng <- range(x, na.rm = TRUE)
(x - rng[1]) / (rng[2] - rng[1])
}
df <- tibble(x = 1:4, y = rnorm(4))
df %>% mutate(across(where(is.numeric), rescale01))
与summarise()配合
# 分组汇总统计
starwars %>%
group_by(species) %>%
filter(n() > 1) %>%
summarise(across(c(sex, gender, homeworld), n_distinct))
与filter()配合的特殊函数
across()不能直接用于filter(),需要使用专门的辅助函数:
# if_any(): 任一列满足条件
starwars %>% filter(if_any(everything(), ~ !is.na(.x)))
# if_all(): 所有列都满足条件
starwars %>% filter(if_all(everything(), ~ !is.na(.x)))
# 具体条件筛选
df <- tibble(x = c("a", "b"), y = c(1, 1), z = c(-1, 1))
df %>% filter(if_all(where(is.numeric), ~ .x > 0)) # 所有数值列都大于0
df %>% filter(if_any(where(is.numeric), ~ .x > 0)) # 任一数值列大于0
高级功能:数据框解包
.unpack参数允许解包函数返回的数据框列,将其展开为多个单独列。
# 定义返回数据框的函数
quantile_df <- function(x, probs = c(0.25, 0.5, 0.75)) {
tibble(quantile = probs, value = quantile(x, probs))
}
# 默认不解包
iris %>% reframe(across(starts_with("Sepal"), quantile_df))
# 自动解包
iris %>% reframe(across(starts_with("Sepal"), quantile_df, .unpack = TRUE))
# 自定义解包命名
iris %>% reframe(across(starts_with("Sepal"), quantile_df,
.unpack = "{outer}.{inner}"))
实际应用场景
场景1:数据质量检查
# 检查每列的缺失值情况
missing_summary <- function(x) {
list(
n_missing = sum(is.na(x)),
pct_missing = mean(is.na(x)) * 100,
unique_values = n_distinct(x, na.rm = TRUE)
)
}
starwars %>% summarise(across(everything(), missing_summary))
场景2:多变量标准化
# 对多个变量进行Z-score标准化
standardize <- function(x) {
(x - mean(x, na.rm = TRUE)) / sd(x, na.rm = TRUE)
}
iris %>% mutate(across(where(is.numeric), standardize))
场景3:批量创建衍生变量
# 为每个数值变量创建对数变换版本
iris %>% mutate(across(where(is.numeric), list(log = ~ log(.x + 1))))
常见陷阱与解决方案
陷阱1:分组变量被意外操作
df <- data.frame(g = c(1, 1, 2), x = c(-1, 1, 3), y = c(-1, -4, -9))
df %>%
group_by(g) %>%
summarise(across(where(is.numeric), sum))
# 结果:g列不会被操作,因为它是分组变量
陷阱2:新创建的列被后续操作选中
# 错误示例:新创建的min_*列会被第二个across()选中
starwars %>% summarise(
across(where(is.numeric), ~min(.x, na.rm = TRUE), .names = "min_{.col}"),
across(where(is.numeric), ~max(.x, na.rm = TRUE), .names = "max_{.col}")
)
# 正确解决方案1:使用tibble包装
starwars %>% summarise(
tibble(
across(where(is.numeric), ~min(.x, na.rm = TRUE), .names = "min_{.col}"),
across(where(is.numeric), ~max(.x, na.rm = TRUE), .names = "max_{.col}")
)
)
# 正确解决方案2:重新排列列顺序
starwars %>%
summarise(across(where(is.numeric), min_max, .names = "{.fn}.{.col}")) %>%
relocate(starts_with("min"))
陷阱3:常量列的统计计算
df <- data.frame(x = c(1, 2, 3), y = c(1, 4, 9))
# n列会被误操作
df %>% summarise(n = n(), across(where(is.numeric), sd))
# 解决方案:显式排除或调整顺序
df %>% summarise(across(where(is.numeric) & !n, sd), n = n())
性能优化建议
评估时机的重要性
across()内部的代码会对每个列和组的组合都进行一次评估,这在生成随机变量等场景中需要特别注意:
gdf <- tibble(g = c(1, 1, 2, 3), v1 = 10:13, v2 = 20:23) %>% group_by(g)
# 外部:1个正态变量
n <- rnorm(1)
gdf %>% mutate(across(v1:v2, ~ .x + n))
# 内部across():6个正态变量(列数×组数)
gdf %>% mutate(across(v1:v2, ~ .x + rnorm(1)))
使用pick()进行纯列选择
如果只需要选择列而不需要应用函数,推荐使用pick()函数:
# 查找包含特定模式的所有组合
starwars %>% count(pick(contains("color")), sort = TRUE)
# 查找所有不同的颜色组合
starwars %>% distinct(pick(contains("color")))
迁移指南:从旧函数到across()
dplyr之前的_if、_at、_all函数族已被across()取代,迁移方法如下:
# 旧代码
df %>% mutate_if(is.numeric, ~mean(.x, na.rm = TRUE))
df %>% mutate_at(vars(c(x, starts_with("y"))), mean)
df %>% mutate_all(mean)
# 新代码
df %>% mutate(across(where(is.numeric), ~mean(.x, na.rm = TRUE)))
df %>% mutate(across(c(x, starts_with("y")), mean))
df %>% mutate(across(everything(), mean))
总结
across()函数是dplyr中处理列式操作的革命性工具,它:
- 统一了语法:取代了多个旧函数,简化了学习曲线
- 提供了灵活性:支持多种列选择方式和函数应用模式
- 增强了表达能力:能够实现之前无法表达的复杂汇总操作
- 保持了性能:通过智能的评估时机控制确保效率
通过掌握across()函数,你将能够更加高效地处理数据清洗、转换和分析任务,显著提升数据处理的效率和代码的可读性。
记住实践是最好的学习方式,尝试在你的下一个数据项目中应用across()函数,体验它带来的便利和强大功能。
【免费下载链接】dplyr 项目地址: https://gitcode.com/gh_mirrors/dpl/dplyr
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



