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

Dplyr/tidyverse报错:rename_at的.funs需含单个重命名函数而非多个

问题描述

我有如下测试数据:

library(tidyverse)
df <- tibble(
  g1 = c(1, 1, 2, 2, 2),
  g2 = c("a", "a", "a", "b", "b"), # 注意原代码中字符串未加引号,这里修正避免报错
  a = sample(5),
  b = sample(5)
)

我希望编写一个函数对分组列进行均值汇总,并将结果列前缀设置为"mean_",编写的函数如下:

my_summarise1 <- function(df, group_var, summarise_var) {
  df %>%
    group_by_at(.vars = group_var) %>%
    summarise_at(.vars = summarise_var, .funs= mean) %>%
    rename_at(.vars= summarise_var, .funs=paste('mean_', .))
}

移除rename_at语句时函数可正常运行,但添加后执行my_summarise1(df, vars(g1,g2),vars(a,b))会报错:

Error: `.funs` must contain one renaming function, not 4

请问如何有效给新列名添加前缀?另外一个小问题:调用函数时能否避免使用vars()或给列名加引号?

解决方案

一、给汇总列添加"mean_"前缀的正确方法

你遇到的报错是因为rename_at的.funs参数需要接收一个函数,直接写paste('mean_', .)的写法不符合要求——这里的.会被识别为当前列名,但paste本身不是返回函数的正确写法。下面两种方法都能解决这个问题:

方法1:修正rename_at的写法

用公式语法(~)把paste包装成匿名函数,明确告诉dplyr如何处理列名:

my_summarise1 <- function(df, group_var, summarise_var) {
  df %>%
    group_by_at(.vars = group_var) %>%
    summarise_at(.vars = summarise_var, .funs = mean) %>%
    rename_at(.vars = summarise_var, .funs = ~paste('mean_', ., sep = "")) # sep参数避免列名出现空格
}

方法2:改用rename_with(推荐,dplyr 1.0.0+版本标准写法)

rename_with是dplyr更新后推荐的重命名函数,语法更直观:

my_summarise1 <- function(df, group_var, summarise_var) {
  df %>%
    group_by_at(.vars = group_var) %>%
    summarise_at(.vars = summarise_var, .funs = mean) %>%
    rename_with(.fn = ~paste('mean_', ., sep = ""), .cols = summarise_var)
}

测试调用后,就能得到带mean_前缀的汇总列了。

二、避免使用vars()或引号调用函数

可以利用dplyr的**tidy评估(tidy evaluation)**特性,让函数支持直接传入裸列名(不用vars()或引号),甚至能省掉重命名的步骤,写法更简洁:

改进后的函数(支持裸列名)

my_summarise2 <- function(df, ..., summarise_var) {
  df %>%
    group_by(...) %>% # ... 接收任意数量的分组裸列名
    # 用across的.names参数直接指定列名格式,{.col}会替换成原列名
    summarise(across({{ summarise_var }}, mean, .names = "mean_{.col}"))
}

调用方式(直接传裸列名)

# 单个汇总变量
my_summarise2(df, g1, g2, summarise_var = a)

# 多个汇总变量,用c()包裹
my_summarise2(df, g1, g2, summarise_var = c(a, b))

这里的核心技巧:

  • ...用来接收任意数量的分组变量,直接传裸列名即可;
  • {{ summarise_var }}是大括号语法,用来捕获用户传入的裸列名(单个或多个用c()包裹);
  • across的.names参数可以直接定义输出列名的模板,连额外的重命名步骤都省了,非常高效。

内容的提问来源于stack exchange,提问作者Jacek Kotowski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:03:34