如何在dplyr中使用across()调用返回多列的自定义函数?
关于dplyr中across()处理返回多列自定义函数的问题
我正在探索dplyr新版本的across()函数,想了解如何用它调用返回多列的自定义函数。具体需求是:对数据框中指定的数值列,按分组计算均值和标准差,并将结果作为单独列返回。
示例数据
library(dplyr) df <- data.frame( Group = rep(letters[1:3], each = 4), Value1 = rnorm(12, mean = 10, sd = 2), Value2 = rnorm(12, mean = 5, sd = 1) )
期望输出
Group Mean_Value1 SD_Value1 Mean_Value2 SD_Value2 1 a 9.812 2.034 4.955 1.085 2 b 10.231 1.987 5.023 0.923 3 c 10.032 2.121 4.998 1.098
尝试的代码(报错)
df_summary <- df %>% group_by(Group) %>% summarise(across(starts_with("Value"), ~ c(mean = mean(.), sd = sd(.))))
报错原因是across()无法直接处理返回多值的函数,以下是具体问题:
- 如何修改上述方法,让
across()正确处理返回多值的自定义函数? - 使用dplyr或R中其他包,是否有更优的实现方式?
- 处理此类自定义函数时,
across()存在哪些局限性?
解答
1. 修改现有代码让across()支持多列返回
要让across()处理返回多列的函数,有两种简洁的修改方式:
方法一:使用命名函数列表
直接在across中传入包含mean和sd的命名列表,dplyr会自动为每个原列生成带后缀的新列:
df_summary <- df %>% group_by(Group) %>% summarise(across(starts_with("Value"), list(Mean = mean, SD = sd)), .groups = "drop")
如果需要将列名格式从Value1_Mean改成Mean_Value1,可以用rename_with调整:
df_summary <- df %>% group_by(Group) %>% summarise(across(starts_with("Value"), list(Mean = mean, SD = sd)), .groups = "drop") %>% rename_with(~ gsub("(.*)_(.*)", "\\2_\\1", .x), -Group)
方法二:自定义返回tibble的函数
如果需要更复杂的统计逻辑,可以写一个返回tibble的函数,再结合unnest展开结果:
summarise_stats <- function(x) { tibble(Mean = mean(x, na.rm = TRUE), SD = sd(x, na.rm = TRUE)) } df_summary <- df %>% group_by(Group) %>% summarise(across(starts_with("Value"), summarise_stats), .groups = "drop") %>% unnest(cols = starts_with("Value"))
2. 更优的实现方式
dplyr内置方式(推荐)
上述方法一已经是dplyr中最简洁的实现,无需额外自定义函数,代码可读性和维护性都很高。
使用data.table(大数据场景)
如果处理超大型数据集,data.table的分组统计效率更高:
library(data.table) setDT(df) df_summary <- df[, lapply(.SD, function(x) .(Mean = mean(x), SD = sd(x))), by = Group, .SDcols = starts_with("Value")] df_summary <- melt(df_summary, id.vars = "Group") %>% dcast(Group ~ variable + L1)
使用skimr包(快速生成多维度统计)
如果需要更多统计指标(如中位数、分位数),skimr可以一键生成结构化摘要:
library(skimr) df %>% group_by(Group) %>% skim(starts_with("Value")) %>% select(Group, variable, mean, sd) %>% pivot_wider(names_from = variable, values_from = c(mean, sd), names_glue = "{.value}_{variable}")
3. across()处理多列返回函数的局限性
- 默认不支持向量型多值返回:如果函数返回普通向量(如
c(mean, sd)),across会将其解析为多行而非多列,必须用返回tibble的函数或命名列表规避。 - 列名格式需手动调整:默认生成的列名是
原列名_函数名,自定义格式需要额外用rename_with处理。 - 复杂函数的结构一致性要求高:自定义函数返回的
tibble必须保持统一结构,否则unnest会报错。 - 大数据场景性能略逊:对于超大规模数据集,
across的多列处理效率略低于手动指定列的原生分组操作,但常规场景下差异可忽略。
内容的提问来源于stack exchange,提问作者Antreas Stefopoulos
相关产品推荐
相关产品推荐

