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

如何在data.table中使用rlang包的{{双大括号运算符

问题描述

rlang包提供的{{}}运算符(又称准引用Quasiquotation)可极大简化将列名作为函数参数传递的实现。已知rlang包主要为tidyverse生态设计,请问是否存在方法可在data.table中使用{{}}运算符?

{{}}配合dplyr的预期用法
test_dplyr <- function(dt, col1, col2){
  
  temp <- dt %>%
            group_by( {{col2}} ) %>%
            summarise(test = mean( {{col1}} ))

  return(temp)
}

test_dplyr(dt=iris, col1=Sepal.Length, col2=Species)

运行返回结果:

# A tibble: 3 x 2
  Species     test
  <fct>      <dbl>
1 setosa      5.01
2 versicolor  5.94
3 virginica   6.59
在data.table中使用{{}}的失败尝试

以下是理想中想要实现的写法,但运行会返回错误。

test_dt2 <- function(dt, col1, col2){
  
  data.table::setDT(dt)
  temp <- dt[, .( test = mean({{col1}})), by = {{col2}} ]
  return(temp)
}

# 报错
test_dt2(dt=iris, col1= Sepal.Length, col2= Species)

# 同样报错
test_dt2(dt=iris, col1= 'Sepal.Length', col2= 'Species')
rlang配合data.table的替代用法

以下是目前可实现rlang与data.table结合使用的替代方案,该方案存在两处不便:一是需要对每个列名变量调用rlang::ensym()处理,二是必须将data.table操作放在rlang::inject()内执行。

test_dt <- function(dt, col1, col2){
  
  # 处理列名
  col1 <- rlang::ensym(col1)
  col2 <- rlang::ensym(col2)
  
  data.table::setDT(dt)
  temp <- rlang::inject( dt[, .( test = mean(!!col1)), by = !!col2] )
  return(temp)
}

test_dt(dt=iris, col1='Sepal.Length', col2='Species')

运行返回结果:

Species  test
1:     setosa 5.006
2: versicolor 5.936
3:  virginica 6.588

解决方案

完全可以直接在data.table中使用{{}}语法,不需要逐个参数调用ensym()处理,只需要把包含{{}}的data.table查询语句整体放到rlang::inject()内部即可:

test_dt_better <- function(dt, col1, col2){
  data.table::setDT(dt)
  temp <- rlang::inject(
    dt[, .(test = mean({{col1}})), by = {{col2}}]
  )
  return(temp)
}

这个写法同时兼容裸列名和字符串列名两种传参方式,和dplyr的使用体验完全一致:

  • 传裸列名:test_dt_better(iris, Sepal.Length, Species) 正常运行
  • 传字符串列名:test_dt_better(iris, "Sepal.Length", "Species") 正常运行
    返回结果和之前的替代方案完全相同。

报错原因

{{}}是tidy eval体系的专属语法糖,本质是!!enquo(参数)的简写,只有原生支持tidy eval的函数(比如dplyr的所有数据处理函数)会自动解析这个语法。data.table本身不识别tidy eval规则,直接写{{}}会被当成普通R代码执行,自然会抛出错误。
rlang::inject()的作用就是在代码正式执行前,先把包裹范围内的所有tidy eval语法(包括{{}}、!!、!!!)完成解析替换,替换后的代码再交给data.table执行,就不会出现语法不兼容的问题。

如果日常高频使用,可以写个简单的辅助函数减少重复输入:

dt_tidy <- function(expr) {
  rlang::inject({{ expr }})
}

# 调用示例
test_dt_short <- function(dt, col1, col2){
  data.table::setDT(dt)
  dt_tidy(dt[, .(test = mean({{col1}})), by = {{col2}}])
}

内容的提问来源于stack exchange,提问作者rafa.pereira

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 09:27:29