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

在R中使用tidyverse构建自定义函数——enquo()无法正常工作

解决自定义分组计算RMSE函数的报错问题

我有一个包含预测值(pred)、观测值(obs)和数据集分组(datasets)的数据框,直接用tidyverse分组计算RMSE可以正常运行:

datasets = rep(1:4, each=5)
obs = runif(n=20, min=1, max=20)
pred = runif(n=20, min=1, max=20)
df = data.frame(datasets,obs,pred)

library(tidyverse)
library(hydroGOF)

# 正常运行的代码
df %>%
    group_by(datasets) %>%
    reframe(result = rmse(obs,pred))

但自定义函数调用时却报错,函数代码和调用方式如下:

# 报错的自定义函数
make_calc = function(df,x,y,group){
    library(tidyverse)
    library(hydroGOF)
        
    obs = enquo(x)
    pred = enquo(y)
    group= enquo(group)
    
    some_calc = df %>%
        group_by(!!group) %>%
        reframe(result = rmse(!!obs,!!pred))
    
    return(some_calc)
}

make_calc(df,'obs','pred','datasets')

报错信息:

Error in `reframe()`:
ℹ In argument: `result = rmse("obs", "pred")`.
ℹ In group 1: `"datasets" = "datasets"`.
Caused by error in `rmse.default()`:
! Invalid argument type: 'sim' & 'obs' have to be of class: c('integer', 'numeric', 'ts', 'zoo')
Run `rlang::last_trace()` to see where the error occurred.

错误原因

问题出在参数传递和引用的不匹配:

  • enquo()的作用是捕获传入的表达式/裸变量名,但调用函数时传的是字符串(比如'obs'),enquo('obs')会把字符串本身当成值,而不是引用数据框中的obs列。
  • 这导致rmse()拿到的是字符串而非数值向量,触发类型错误;同时group_by(!!group)会把字符串'datasets'当成一个常量值,从而新增一列而非按原有的datasets列分组。

解决方案

有两种常用的修正方式,根据参数传递习惯选择:

方法1:使用裸变量名传参(tidyverse推荐)

修改函数,用{{ }}(embrace语法)替代enquo()和!!,同时调用函数时直接传列名(不用引号):

make_calc = function(df, x, y, group){
    library(tidyverse)
    library(hydroGOF)
    
    some_calc = df %>%
        group_by({{ group }}) %>%
        reframe(result = rmse({{ x }}, {{ y }}))
    
    return(some_calc)
}

# 调用时直接传裸列名
make_calc(df, obs, pred, datasets)

方法2:支持字符串传参

如果需要保留字符串传参的方式,用ensym()替代enquo(),它可以将字符串转换为对应的列名符号:

make_calc = function(df, x, y, group){
    library(tidyverse)
    library(hydroGOF)
    
    obs = ensym(x)
    pred = ensym(y)
    group_col = ensym(group)
    
    some_calc = df %>%
        group_by(!!group_col) %>%
        reframe(result = rmse(!!obs, !!pred))
    
    return(some_calc)
}

# 保持字符串传参
make_calc(df, 'obs', 'pred', 'datasets')

两种方法都能正确按datasets分组,并计算每组的RMSE,得到和直接运行代码一致的结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 00:33:26