一个简单的可复制示例,用于在R中的自定义函数中将参数传递给data.table

一个简单的可复制示例,用于在R中的自定义函数中将参数传递给data.table,r,function,data.table,R,Function,Data.table,我已经在谷歌上搜索了几个小时这个答案。很多人问过类似的问题,但我没有找到足够简单的问题或直接的答案。我的做法如下: 假设我想在数据中做一个简单的分组 library(data.table) mtcars = data.table(mtcars) mtcars[,sum(mpg), gear] # Here are the results # gear V1 #1: 4 294.4 #2: 3 241.6 #3: 5 106.9 但是,如果我使用自定义函数执行此操作

我已经在谷歌上搜索了几个小时这个答案。很多人问过类似的问题,但我没有找到足够简单的问题或直接的答案。我的做法如下:

假设我想在
数据中做一个简单的分组

library(data.table)
mtcars = data.table(mtcars)
mtcars[,sum(mpg), gear]

# Here are the results
#   gear    V1
#1:    4 294.4
#2:    3 241.6
#3:    5 106.9
但是,如果我使用自定义函数执行此操作:

zz = function(data, var, group){
  return(data[,sum(var), group])
}
zz(mtcars, mpg, gear)
我收到一条错误消息:

eval中出错(bysub,parent.frame(),parent.frame()): 找不到对象“齿轮”

我尝试过
substitute
eval
quote
,以及其他解决方案,但没有一个有效。我想知道是否有人能给出一个更直接的解决方案和解释


谢谢你,万圣节快乐

如果我们使用的是不带引号的参数,
substitute
eval
uate

zz <- function(data, var, group){
 var <- substitute(var)
 group <- substitute(group)
 setnames(data[, sum(eval(var)), by = group],
        c(deparse(group), deparse(var)))[]
 # or use
 #  setnames(data[, sum(eval(var)), by = c(deparse(group))], 2, deparse(var))[]

}
zz(mtcars, mpg, gear)
#   gear   mpg
#1:    4 294.4
#2:    3 241.6
#3:    5 106.9

zz虽然不是完美的,
..
参数可能会有帮助:

zz = function(dt, ...){
  return(dt[...])
}

zz(mtcars, , sum(mpg), gear)

   gear    V1
1:    4 294.4
2:    3 241.6
3:    5 106.9

我真的不认为编写一个接受无引号参数的函数有什么意义。为什么不直接使用data.table语法呢

如果您想编写一个函数,那么采用列名的字符向量更有意义,因为这比符号更易于编程(考虑使用
setkey
setkeyv
编程,或者使用
aes
aes\u字符串
编写函数来创建ggplot)缺点是函数的内部比较混乱,需要eval(parse(text=))NSE才能使GForce正常工作,但函数接口更具可扩展性

zz = function(data, var, group){
  eval(parse(text=paste0("data[,sum(",var,"),by=",group,"]")))
}
zz(mtcars, "mpg", "gear")

@Gregor我猜它可能与
env
替换(expr,env)
有关,因为
替换将检查data.table的env中的变量。你可以试着改变
env
,这很有意义,但是玩了一点之后,我还是无法让它工作。“我想我得问个问题……”格雷戈,我也试过了。我记得以前有过类似的问题,但没有发现。请把它当作一个问题来问。谢谢这里有一个新问题:(现在我希望我没有删除原来的评论)@Gregor谢谢你!嘿,迈克尔!这绝对是一个有趣的方法。我自己并没有写很多这样的函数,但我确实看到了无引号参数对于交互式使用的吸引力,尤其是在制表符完成时。eval parse paste仍然让我感觉很糟糕,尽管我无法识别这种情况下的任何特定风险。好吧,
by
参数可以是一个带引号的字符串——可以是列名的字符向量,也可以是逗号分隔的名称的长度为一个字符的向量,但是我认为您使用
.SDcols
是正确的效率较低。然而,
x=c(“mpg”,“hp”)
g=c(“am”,“cyl”)
,然后
mtcars[,lappy(.SD,平均值),by=g,.SDcols=g)
写起来很好。但肯定没有允许用户输入任意字符串那么灵活。与此同时,akrun在我键入此评论时刚刚回答了…是的,.SD方法也是我尝试过的方法,几乎满足了我的任意编程标准(它在i语句中不起作用,您还需要做一些其他的技巧来获得LHS赋值中的任意列名)。最终,我反对使用.SD方法进行编程,因为它需要跟踪.SD的索引,而对于复杂的j语句,我发现很难遵循这种方法(甚至比宏更重要,宏至少可以通过评估粘贴来调试,以获得非常可读的数据。表语法)例如,单独重复地获取data.table的第1列和任意数量的其他列之间的乘积,而不使用一些挑剔的.SD索引和lappy,这可能并不简单。