You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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()无法直接处理返回多值的函数,以下是具体问题:

  1. 如何修改上述方法,让across()正确处理返回多值的自定义函数?
  2. 使用dplyr或R中其他包,是否有更优的实现方式?
  3. 处理此类自定义函数时,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 18:44:57