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

R语言传递dplyr代码为函数参数时出现报错的解决咨询

解决dplyr代码作为函数参数的执行错误

问题背景

需求是创建一个函数,允许传入dplyr代码作为参数,根据数据框是否包含category列自动执行对应逻辑:无category列执行code1,有则执行code2。

已构建的数据集:

library(dplyr)

start_date <- as.Date("2024-01-01")
end_date <- Sys.Date()
dates <- seq(from = start_date, to = end_date, by = "week")
states <- c("California", "Texas", "Florida", "New York", "Illinois", "Ohio", "Georgia", "North Carolina", "Michigan", "Pennsylvania")
category <- c("A", "B", "C")

df1 <- expand.grid(date = dates, state = sample(states, length(dates), replace = TRUE)) %>%
  mutate(
    units = sample(100:1000, nrow(.), replace = TRUE),
    amount = round(runif(nrow(.), min = 1000, max = 10000), 2)
  )

df2 <- expand.grid(date = dates, state = sample(states, length(dates), replace = TRUE), category = sample(category, length(dates), replace = TRUE)) %>%
  mutate(
    units = sample(100:1000, nrow(.), replace = TRUE),
    amount = round(runif(nrow(.), min = 1000, max = 10000), 2)
  )

原函数及调用代码:

# 原函数
apply_conditional_summary <- function(data, code1, code2) {
  if ("category" %in% colnames(data)) {
    result <- eval(substitute(data %>% code2))
  } else {
    result <- eval(substitute(data %>% code1))
  }
  return(result)
}

# 调用时触发错误
apply_conditional_summary(
  df1, 
  group_by(date, state) %>% summarise(units = mean(units), total_amt = sum(amount), .groups = "drop"),
  group_by(date, state, category) %>% 
    summarise(units = mean(units), total_amt = sum(amount), .groups = "drop") %>%
    group_by(date, state) %>%
    mutate(share = total_amt / sum(total_amt)) %>%
    ungroup()
)

报错信息:

Error in `%>%`(., group_by(date, state), summarise(units = mean(units),  : 
  unused argument (summarise(units = mean(units), total_amt = sum(amount), .groups = "drop"))

错误原因

原函数中,传入的code1/code2是已经用%>%连接的完整表达式,但执行eval(substitute(data %>% code1))时,相当于把data传入code1的第一个管道步骤后,又把整个后续链当作额外参数传递,违背了管道操作的语法规则,导致group_by等函数收到未定义的参数,触发报错。

解决方法

推荐使用tidyverse的**整洁评估(tidyeval)**机制处理这类场景,比直接用eval(substitute())更稳定可靠。

方案1:使用enquo捕获表达式(推荐)

library(dplyr)

apply_conditional_summary <- function(data, code1, code2) {
  # 捕获传入的dplyr表达式
  code1 <- enquo(code1)
  code2 <- enquo(code2)
  
  if ("category" %in% colnames(data)) {
    data %>% !!code2
  } else {
    data %>% !!code1
  }
}

正确调用示例

传入的code1/code2只需写从dplyr操作开始的部分(不需要带开头的data %>%):

# 调用无category列的df1
apply_conditional_summary(
  df1, 
  group_by(date, state) %>% summarise(units = mean(units), total_amt = sum(amount), .groups = "drop"),
  group_by(date, state, category) %>% 
    summarise(units = mean(units), total_amt = sum(amount), .groups = "drop") %>%
    group_by(date, state) %>%
    mutate(share = total_amt / sum(total_amt)) %>%
    ungroup()
)

# 调用有category列的df2
apply_conditional_summary(
  df2, 
  group_by(date, state) %>% summarise(units = mean(units), total_amt = sum(amount), .groups = "drop"),
  group_by(date, state, category) %>% 
    summarise(units = mean(units), total_amt = sum(amount), .groups = "drop") %>%
    group_by(date, state) %>%
    mutate(share = total_amt / sum(total_amt)) %>%
    ungroup()
)

方案2:使用rlang::expr包裹传入代码

如果不想用enquo,也可以让调用者用expr()包裹代码,函数内用eval_tidy执行:

library(dplyr)
library(rlang)

apply_conditional_summary <- function(data, code1, code2) {
  if ("category" %in% colnames(data)) {
    eval_tidy(expr(data %>% !!code2), data = list(data = data))
  } else {
    eval_tidy(expr(data %>% !!code1), data = list(data = data))
  }
}

# 调用示例:用expr包裹代码
apply_conditional_summary(
  df1, 
  expr(group_by(date, state) %>% summarise(units = mean(units), total_amt = sum(amount), .groups = "drop")),
  expr(group_by(date, state, category) %>% 
         summarise(units = mean(units), total_amt = sum(amount), .groups = "drop") %>%
         group_by(date, state) %>%
         mutate(share = total_amt / sum(total_amt)) %>%
         ungroup())
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 14:52:35