如何在data.table中引用与列同名的函数参数?
data.table子集操作作用域冲突解决方案
问题场景
现有测试用data.table对象foo定义如下:
foo <- data.table(t = c(1,1,2,2,3), b = rnorm(5)) foo # t b # 1: 1 0.07014277 # 2: 1 1.71144087 # 3: 2 -0.60290798 # 4: 2 -0.47216639 # 5: 3 -0.63537131
自定义函数myfunc()初始代码如下:
myfunc <- function(dt, t){ # 按t值对dt取子集 dt <- dt[J(t = t), by = "t"] # 后续复杂计算/绘图逻辑 score <- mean(dt$b) return(score) }
函数入参说明:
dt:待操作的data.table对象t:子集筛选的取值,对应筛选dt中名为t的列
问题描述
执行子集操作dt <- dt[J(t = t), by = "t"]时,data.table会优先在表内部的列中查找t,导致等号右侧的t无法被识别为函数作用域传入的参数t,尝试dt <- dt[J(t = get(t, -1)), by = "t"]等写法均未生效。
直接修改函数参数名可以规避该问题,但实际场景中函数参数名有明确语义、长度较长不宜修改,且函数内部逻辑复杂重构成本高,需要在不大幅改动原有代码的前提下解决作用域冲突。
可行解决方案
以下方案均不需要修改原有函数参数名,也不需要改动后续复杂业务逻辑,仅需调整子集行的代码或新增1行临时变量赋值即可:
- 使用data.table官方支持的
..前缀(推荐,适配新版本data.table)..前缀用于显式告诉data.table,要从当前操作环境的上一级(即函数环境)查找变量,不会和表内列名冲突:myfunc <- function(dt, t){ dt <- dt[J(t = ..t), by = "t"] # 原有逻辑完全不需要改动 score <- mean(dt$b) return(score) } - 临时变量中转(兼容性最强,适配所有data.table版本)
在子集操作前加1行代码,将入参t赋值给一个不会和表列名重名的临时变量,从根源上避免命名冲突,后续逻辑完全不需要调整:myfunc <- function(dt, t){ # 仅新增这1行即可 .filter_t <- t dt <- dt[J(t = .filter_t), by = "t"] # 原有逻辑完全不需要改动 score <- mean(dt$b) return(score) } - 显式指定
get的查找环境
如果需要用get取值,显式指定查找环境为当前函数环境即可,不要依赖默认的作用域查找顺序:myfunc <- function(dt, t){ cur_env <- environment() dt <- dt[J(t = get("t", envir = cur_env)), by = "t"] score <- mean(dt$b) return(score) }
以上写法测试调用myfunc(foo, t = 1)均可正确返回t=1分组下b列的均值,不会出现取值错误或对象找不到的报错。
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

